← Back to team overview

openerp-community-reviewer team mailing list archive

[Merge] lp:~openerp-community/openerp-hr/7.0-modules-from-addons-hr-ng into lp:openerp-hr

 

Maxime Chambreuil (http://www.savoirfairelinux.com) has proposed merging lp:~openerp-community/openerp-hr/7.0-modules-from-addons-hr-ng into lp:openerp-hr.

Requested reviews:
  HR Core Editors (hr-core-editors)

For more details, see:
https://code.launchpad.net/~openerp-community/openerp-hr/7.0-modules-from-addons-hr-ng/+merge/188169
-- 
The attached diff has been truncated due to its size.
https://code.launchpad.net/~openerp-community/openerp-hr/7.0-modules-from-addons-hr-ng/+merge/188169
Your team HR Core Editors is requested to review the proposed merge of lp:~openerp-community/openerp-hr/7.0-modules-from-addons-hr-ng into lp:openerp-hr.
=== added directory 'hr_accrual'
=== added file 'hr_accrual/__init__.py'
--- hr_accrual/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_accrual/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_accrual

=== added file 'hr_accrual/__openerp__.py'
--- hr_accrual/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_accrual/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,54 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Accrual',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Accruals
+========
+
+An Accrual is any benefit (usually time) that accrues on behalf of an employee over an extended
+period of time. This can be vacation days, sick days, or a simple time bank. The actual policy
+and mechanics of accrual should be handled by other modules. This module only provides
+the basic framework for recording the data.
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+        'hr_holidays',
+        'hr_holidays_extension',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'hr_accrual_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_accrual/hr_accrual.py'
--- hr_accrual/hr_accrual.py	1970-01-01 00:00:00 +0000
+++ hr_accrual/hr_accrual.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,70 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import time
+
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DATEFORMAT
+
+
+class hr_accrual(osv.Model):
+
+    _name = 'hr.accrual'
+    _description = 'Accrual'
+
+    _columns = {
+        'name': fields.char('Name', size=128, required=True),
+        'holiday_status_id': fields.many2one('hr.holidays.status', 'Leave'),
+        'line_ids': fields.one2many('hr.accrual.line', 'accrual_id', 'Accrual Lines', readonly=True),
+    }
+
+    def get_balance(self, cr, uid, ids, employee_id, date=None, context=None):
+
+        if date == None:
+            date = time.strftime(OE_DATEFORMAT)
+
+        res = 0.0
+        cr.execute('''SELECT SUM(amount) from hr_accrual_line \
+                           WHERE accrual_id in %s AND employee_id=%s AND date <= %s''',
+                   (tuple(ids), employee_id, date))
+        for row in cr.fetchall():
+            res = row[0]
+
+        return res
+
+
+class hr_accrual_line(osv.Model):
+
+    _name = 'hr.accrual.line'
+    _description = 'Accrual Line'
+
+    _columns = {
+        'date': fields.date('Date', required=True),
+        'accrual_id': fields.many2one('hr.accrual', 'Accrual', required=True),
+        'employee_id': fields.many2one('hr.employee', 'Employee', required=True),
+        'amount': fields.float('Amount', required=True),
+    }
+
+    _defaults = {
+        'date': time.strftime(OE_DATEFORMAT),
+    }
+
+    _rec_name = 'date'

=== added file 'hr_accrual/hr_accrual_view.xml'
--- hr_accrual/hr_accrual_view.xml	1970-01-01 00:00:00 +0000
+++ hr_accrual/hr_accrual_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="hr_accrual_view_tree" model="ir.ui.view">
+            <field name="name">hr.accrual.tree</field>
+            <field name="model">hr.accrual</field>
+            <field name="arch" type="xml">
+                <tree string="Accruals">
+                    <field name="name"/>
+                    <field name="holiday_status_id"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="hr_accrual_view_form" model="ir.ui.view">
+            <field name="name">hr.accrual.form</field>
+            <field name="model">hr.accrual</field>
+            <field name="arch" type="xml">
+                <form string="Accrual" version="7.0">
+                    <label for="name" string="Name" class="oe_edit_only"/>
+                    <h1>
+                        <field name="name"/>
+                    </h1>
+                    <field name="holiday_status_id"/>
+                    <group string="Accrual Lines">
+                        <field name="line_ids" nolabel="1"/>
+                    </group>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_accrual" model="ir.actions.act_window">
+            <field name="name">Accrual Time Banks</field>
+            <field name="res_model">hr.accrual</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="open_accrual"
+            id="menu_hr_accrual"
+            parent="hr.menu_hr_configuration"
+            sequence="45"/>
+        
+    </data>
+</openerp>

=== added directory 'hr_accrual/security'
=== added file 'hr_accrual/security/ir.model.access.csv'
--- hr_accrual/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_accrual/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_accrual_manager,access_hr_accrual,model_hr_accrual,base.group_hr_manager,1,1,1,1
+access_hr_accrual_line_manager,access_hr_accrual_line,model_hr_accrual_line,base.group_hr_manager,1,1,1,1
+access_hr_accrual_leave_approval,access_hr_accrual,model_hr_accrual,hr_holidays_extension.group_hr_leave,1,1,1,1
+access_hr_accrual_line_leave_approval,access_hr_accrual_line,model_hr_accrual_line,hr_holidays_extension.group_hr_leave,1,1,1,1

=== added directory 'hr_contract_init'
=== added file 'hr_contract_init/__init__.py'
--- hr_contract_init/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_contract_init/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_contract

=== added file 'hr_contract_init/__openerp__.py'
--- hr_contract_init/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_contract_init/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,56 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Contracts - Initial Settings',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Define Initial Settings on New Contracts
+========================================
+    - Starting Wages
+    - Salary Structure
+    - Trial Period Length
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+        'hr_contract',
+        'hr_job_categories',
+        'hr_payroll',
+        'hr_security',
+        'hr_simplify',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'hr_contract_init_workflow.xml',
+        'hr_contract_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_contract_init/hr_contract.py'
--- hr_contract_init/hr_contract.py	1970-01-01 00:00:00 +0000
+++ hr_contract_init/hr_contract.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,252 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from datetime import datetime, timedelta
+from dateutil.relativedelta import relativedelta
+
+from openerp import netsvc
+from openerp.addons import decimal_precision as dp
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DFORMAT
+from openerp.tools.translate import _
+
+
+class contract_init(osv.Model):
+
+    _name = 'hr.contract.init'
+    _description = 'Initial Contract Settings'
+
+    _inherit = 'ir.needaction_mixin'
+
+    _columns = {
+        'name': fields.char('Name', size=64, required=True, readonly=True,
+                            states={'draft': [('readonly', False)]}),
+        'date': fields.date('Effective Date', required=True, readonly=True,
+                            states={'draft': [('readonly', False)]}),
+        'wage_ids': fields.one2many('hr.contract.init.wage', 'contract_init_id',
+                                    'Starting Wages', readonly=True,
+                                    states={'draft': [('readonly', False)]}),
+        'struct_id': fields.many2one('hr.payroll.structure', 'Payroll Structure', readonly=True,
+                                     states={'draft': [('readonly', False)]}),
+        'trial_period': fields.integer('Trial Period', readonly=True,
+                                       states={'draft': [('readonly', False)]},
+                                       help="Length of Trial Period, in days"),
+        'active': fields.boolean('Active'),
+        'state': fields.selection([('draft', 'Draft'),
+                                   ('approve', 'Approved'),
+                                   ('decline', 'Declined')], 'State', readonly=True),
+    }
+
+    _defaults = {
+        'trial_period': 0,
+        'active': True,
+        'state': 'draft',
+    }
+
+    # Return records with latest date first
+    _order = 'date desc'
+
+    def _needaction_domain_get(self, cr, uid, context=None):
+
+        users_obj = self.pool.get('res.users')
+        domain = []
+
+        if users_obj.has_group(cr, uid, 'base.group_hr_director'):
+            domain = [('state', 'in', ['draft'])]
+            return domain
+
+        return False
+
+    def unlink(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+        data = self.read(cr, uid, ids, ['state'], context=context)
+        for d in data:
+            if d['state'] in ['approve', 'decline']:
+                raise osv.except_osv(_('Error'),
+                                     _('You may not a delete a record that is not in a "Draft" state'))
+        return super(contract_init, self).unlink(cr, uid, ids, context=context)
+
+    def set_to_draft(self, cr, uid, ids, context=None):
+        self.write(cr, uid, ids, {
+            'state': 'draft',
+        })
+        wf_service = netsvc.LocalService("workflow")
+        for i in ids:
+            wf_service.trg_delete(uid, 'hr.contract.init', i, cr)
+            wf_service.trg_create(uid, 'hr.contract.init', i, cr)
+        return True
+
+    def state_approve(self, cr, uid, ids, context=None):
+
+        self.write(cr, uid, ids, {'state': 'approve'}, context=context)
+        return True
+
+    def state_decline(self, cr, uid, ids, context=None):
+
+        self.write(cr, uid, ids, {'state': 'decline'}, context=context)
+        return True
+
+
+class init_wage(osv.Model):
+
+    _name = 'hr.contract.init.wage'
+    _description = 'Starting Wages'
+
+    _columns = {
+        'job_id': fields.many2one('hr.job', 'Job'),
+        'starting_wage': fields.float('Starting Wage', digits_compute=dp.get_precision('Payroll'),
+                                      required=True),
+        'is_default': fields.boolean('Use as Default',
+                                     help="Use as default wage"),
+        'contract_init_id': fields.many2one('hr.contract.init', 'Contract Settings'),
+        'category_ids': fields.many2many('hr.employee.category', 'contract_init_category_rel', 'contract_init_id', 'category_id', 'Tags'),
+    }
+
+    _sql_constraints = [('unique_job_cinit', 'UNIQUE(job_id,contract_init_id)', _(
+        'A Job Position cannot be referenced more than once in a Contract Settings record.'))]
+
+    def unlink(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+        data = self.read(cr, uid, ids, ['contract_init_id'], context=context)
+        for d in data:
+            if not d.get('contract_init_id', False):
+                continue
+            d2 = self.pool.get(
+                'hr.contract.init').read(cr, uid, d['contract_init_id'][0],
+                                         ['state'], context=context)
+            if d2['state'] in ['approve', 'decline']:
+                raise osv.except_osv(_('Error'),
+                                     _('You may not a delete a record that is not in a "Draft" state'))
+        return super(init_wage, self).unlink(cr, uid, ids, context=context)
+
+
+class hr_contract(osv.Model):
+
+    _inherit = 'hr.contract'
+
+    def _get_wage(self, cr, uid, context=None, job_id=None):
+
+        res = 0
+        default = 0
+        init = self.get_latest_initial_values(cr, uid, context=context)
+        if job_id:
+            catdata = self.pool.get('hr.job').read(
+                cr, uid, job_id, ['category_ids'], context=context)
+        else:
+            catdata = False
+        if init != None:
+            for line in init.wage_ids:
+                if job_id != None and line.job_id.id == job_id:
+                    res = line.starting_wage
+                elif catdata:
+                    cat_id = False
+                    category_ids = [c.id for c in line.category_ids]
+                    for ci in catdata['category_ids']:
+                        if ci in category_ids:
+                            cat_id = ci
+                            break
+                    if cat_id:
+                        res = line.starting_wage
+                if line.is_default and default == 0:
+                    default = line.starting_wage
+                if res != 0:
+                    break
+        if res == 0:
+            res = default
+        return res
+
+    def _get_struct(self, cr, uid, context=None):
+
+        res = False
+        init = self.get_latest_initial_values(cr, uid, context=context)
+        if init != None and init.struct_id:
+            res = init.struct_id.id
+        return res
+
+    def _get_trial_date_start(self, cr, uid, context=None):
+
+        res = False
+        init = self.get_latest_initial_values(cr, uid, context=context)
+        if init != None and init.trial_period and init.trial_period > 0:
+            res = datetime.now().strftime(OE_DFORMAT)
+        return res
+
+    def _get_trial_date_end(self, cr, uid, context=None):
+
+        res = False
+        init = self.get_latest_initial_values(cr, uid, context=context)
+        if init != None and init.trial_period and init.trial_period > 0:
+            dEnd = datetime.now().date() + timedelta(days=init.trial_period)
+            res = dEnd.strftime(OE_DFORMAT)
+        return res
+
+    _defaults = {
+        'wage': _get_wage,
+        'struct_id': _get_struct,
+        'trial_date_start': _get_trial_date_start,
+        'trial_date_end': _get_trial_date_end,
+    }
+
+    def onchange_job(self, cr, uid, ids, job_id, context=None):
+
+        res = False
+        if job_id:
+            wage = self._get_wage(cr, uid, context=context, job_id=job_id)
+            res = {'value': {'wage': wage}}
+        return res
+
+    def onchange_trial(self, cr, uid, ids, trial_date_start, context=None):
+
+        res = {'value': {'trial_date_end': False}}
+
+        init = self.get_latest_initial_values(cr, uid, context=context)
+        if init != None and init.trial_period and init.trial_period > 0:
+            dStart = datetime.strptime(trial_date_start, OE_DFORMAT)
+            dEnd = dStart + timedelta(days=init.trial_period)
+            res['value']['trial_date_end'] = dEnd.strftime(OE_DFORMAT)
+
+        return res
+
+    def get_latest_initial_values(self, cr, uid, today_str=None, context=None):
+        '''Return a record with an effective date before today_str but greater than all others'''
+
+        init_obj = self.pool.get('hr.contract.init')
+        if today_str == None:
+            today_str = datetime.now().strftime(OE_DFORMAT)
+        dToday = datetime.strptime(today_str, OE_DFORMAT).date()
+
+        res = None
+        ids = init_obj.search(
+            cr, uid, [('date', '<=', today_str), ('state', '=', 'approve')],
+            context=context)
+        for init in init_obj.browse(cr, uid, ids, context=context):
+            d = datetime.strptime(init.date, OE_DFORMAT).date()
+            if d <= dToday:
+                if res == None:
+                    res = init
+                elif d > datetime.strptime(res.date, OE_DFORMAT).date():
+                    res = init
+
+        return res

=== added file 'hr_contract_init/hr_contract_init_workflow.xml'
--- hr_contract_init/hr_contract_init_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_init/hr_contract_init_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+
+        <!-- Workflow Definition -->
+        <record id="wkf_contract_init" model="workflow">
+            <field name="name">hr.contract.init.basic</field>
+            <field name="osv">hr.contract.init</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (Stages) -->
+        
+        <record id="act_draft" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract_init"/>
+            <field name="name">draft</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'draft'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_approve" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract_init"/>
+            <field name="name">approve</field>
+            <field name="kind">function</field>
+            <field name="action">state_approve()</field>
+        </record>
+        
+        <record id="act_decline" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract_init"/>
+            <field name="name">decline</field>
+            <field name="kind">function</field>
+            <field name="action">state_decline()</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="draft2approve" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_approve"/>
+            <field name="signal">signal_approve</field>
+            <field name="group_id" ref="hr_security.group_hr_director"/>
+        </record>
+        
+        <record id="draft2decline" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_decline"/>
+            <field name="signal">signal_decline</field>
+            <field name="group_id" ref="hr_security.group_hr_director"/>
+        </record>
+        
+        <record id="approve2decline" model="workflow.transition">
+            <field name="act_from" ref="act_approve"/>
+            <field name="act_to" ref="act_decline"/>
+            <field name="signal">signal_decline</field>
+            <field name="group_id" ref="hr_security.group_hr_director"/>
+        </record>
+
+    </data>
+</openerp>

=== added file 'hr_contract_init/hr_contract_view.xml'
--- hr_contract_init/hr_contract_view.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_init/hr_contract_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="view_contract_init_tree" model="ir.ui.view">
+            <field name="name">hr.contract.init.tree</field>
+            <field name="model">hr.contract.init</field>
+            <field name="arch" type="xml">
+                <tree string="Contract Initial Values">
+                    <field name="name"/>
+                    <field name="date"/>
+                    <field name="trial_period"/>
+                    <field name="struct_id"/>
+                    <field name="state"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_contract_init_form" model="ir.ui.view">
+            <field name="name">hr.contract.init.form</field>
+            <field name="model">hr.contract.init</field>
+            <field name="arch" type="xml">
+                <form string="Contract Initial Values" version="7.0">
+                    <header>
+                        <button name="signal_approve" type="workflow" states="draft" string="Approve" class="oe_highlight" />
+                        <button name="signal_decline" type="workflow" states="draft,approve" string="Decline" class="oe_highlight" />
+                        <button string="Reset to New" name="set_to_draft" states="decline" type="object" groups="hr_security.group_hr_director"/>
+                        <field name="state" widget="statusbar" statusbar_visible="draft,approve" />
+                    </header>
+                    <label for="name" class="oe_edit_only"/>
+                    <h1>
+                        <field name="name"/>
+                    </h1>
+                    <group>
+                        <group>
+                            <field name="date"/>
+                            <field name="trial_period"/>
+                        </group>
+                        <group>
+                            <field name="struct_id"/>
+                        </group>
+                    </group>
+                    <group string="Initial Wages">
+                        <field name="wage_ids" nolabel="1">
+                            <tree string="Initial Wages" editable="bottom">
+                                <field name="category_ids" widget="many2many_tags"/>
+                                <field name="job_id"/>
+                                <field name="starting_wage"/>
+                                <field name="is_default"/>
+                            </tree>
+                        </field>
+                    </group>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_contract_init" model="ir.actions.act_window">
+            <field name="name">Contract Starting Values</field>
+            <field name="res_model">hr.contract.init</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="open_contract_init"
+            id="menu_hr_contract_init"
+            parent="hr_contract.next_id_56"
+            sequence="10"/>
+        
+        <!-- Initial Wages -->
+        
+        <record id="view_init_wage_tree" model="ir.ui.view">
+            <field name="name">hr.contract.init.wage.tree</field>
+            <field name="model">hr.contract.init.wage</field>
+            <field name="arch" type="xml">
+                <tree string="Initial Wages">
+                    <field name="category_ids" widget="many2many_tags"/>
+                    <field name="job_id"/>
+                    <field name="starting_wage"/>
+                    <field name="is_default"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_init_wage_form" model="ir.ui.view">
+            <field name="name">hr.contract.init.wage.form</field>
+            <field name="model">hr.contract.init.wage</field>
+            <field name="arch" type="xml">
+                <form string="Intial Wages" version="7.0">
+                    <group>
+                        <group>
+                            <field name="job_id"/>
+                            <field name="starting_wage"/>
+                            <field name="is_default"/>
+                        </group>
+                        <group>
+                            <field name="category_ids" widget="many2many_tags"/>
+                        </group>
+                    </group>
+                </form>
+            </field>
+        </record>
+        
+        <!-- Contract -->
+        
+        <record id="hr_contract_view_form" model="ir.ui.view">
+            <field name="name">hr.contract.view.form.contract_init</field>
+            <field name="model">hr.contract</field>
+            <field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='trial_date_start']" position="replace">
+                    <field name="trial_date_start" class="oe_inline" on_change="onchange_trial(trial_date_start)"/> -
+                </xpath>
+            </field>
+        </record>
+        
+        <record id="hr_contract_view_form" model="ir.ui.view">
+            <field name="name">hr.contract.view.form.contract_init</field>
+            <field name="model">hr.contract</field>
+            <field name="inherit_id" ref="hr_simplify.view_contract_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='job_id']" position="replace">
+                    <field name="job_id" required="1" domain="[('department_id', '=', employee_dept_id)]" on_change="onchange_job(job_id)"/>
+                </xpath>
+            </field>
+        </record>
+    
+    </data>
+</openerp>

=== added directory 'hr_contract_init/security'
=== added file 'hr_contract_init/security/ir.model.access.csv'
--- hr_contract_init/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_contract_init/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,7 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_contract_init_hro,access_hr_contract_initial,model_hr_contract_init,base.group_hr_user,1,0,0,0
+access_hr_contract_init_hrd,access_hr_contract_initial,model_hr_contract_init,hr_security.group_hr_director,1,1,1,1
+access_hr_contract_init_payroll,access_hr_contract_initial,model_hr_contract_init,hr_security.group_payroll_manager,1,0,0,0
+access_hr_contract_init_wage_hro,access_hr_contract_initial_wage,model_hr_contract_init_wage,base.group_hr_user,1,0,0,0
+access_hr_contract_init_wage_hrd,access_hr_contract_initial_wage,model_hr_contract_init_wage,hr_security.group_hr_director,1,1,1,1
+access_hr_contract_init_wage_payroll,access_hr_contract_initial_wage,model_hr_contract_init_wage,hr_security.group_payroll_manager,1,0,0,0

=== added directory 'hr_contract_reference'
=== added file 'hr_contract_reference/__init__.py'
--- hr_contract_reference/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_contract_reference/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_contract

=== added file 'hr_contract_reference/__openerp__.py'
--- hr_contract_reference/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_contract_reference/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,44 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    "name": "HR Contract Reference",
+    "version": "1.0",
+    "category": "Generic Modules/Human Resources",
+    "description": """
+HR Contract Reference
+=====================
+This module provides :
+    - Unique reference number for each employee contract
+    - Automatically generated
+    """,
+    "author": "Michael Telahun Makonnen <mmakonnen@xxxxxxxxx",
+    "website": "http://miketelahun.wordpress.com";,
+    "depends": ["hr_contract"],
+    "init_xml": [],
+    'update_xml': [
+        'hr_contract_view.xml',
+        'hr_contract_sequence.xml',
+    ],
+    'demo_xml': [],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_contract_reference/hr_contract.py'
--- hr_contract_reference/hr_contract.py	1970-01-01 00:00:00 +0000
+++ hr_contract_reference/hr_contract.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,41 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from osv import fields, osv
+
+
+class hr_contract(osv.osv):
+
+    _inherit = 'hr.contract'
+
+    _columns = {
+        'name': fields.char('Contract Reference', size=32, required=False, readonly=True),
+    }
+
+    def create(self, cr, uid, vals, context=None):
+
+        cid = super(hr_contract, self).create(cr, uid, vals, context)
+        if cid:
+            ref = self.pool.get('ir.sequence').next_by_code(
+                cr, uid, 'contract.ref', context=context)
+            self.pool.get('hr.contract').write(
+                cr, uid, cid, {'name': ref}, context=context)
+        return cid

=== added file 'hr_contract_reference/hr_contract_sequence.xml'
--- hr_contract_reference/hr_contract_sequence.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_reference/hr_contract_sequence.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        <record id="seq_type_contract_ref" model="ir.sequence.type">
+            <field name="name">Contract Reference</field>
+            <field name="code">contract.ref</field>
+        </record>
+        <record id="seq_contract_ref" model="ir.sequence">
+            <field name="name">Contract Reference</field>
+            <field name="code">contract.ref</field>
+            <field name="prefix">EC/%(year)s/</field>
+            <field name="padding">5</field>
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'hr_contract_reference/hr_contract_view.xml'
--- hr_contract_reference/hr_contract_view.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_reference/hr_contract_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="hr_contract_view" model="ir.ui.view">
+            <field name="name">hr.contract.form.view.inherit.ref</field>
+            <field name="model">hr.contract</field>
+            <field name="type">form</field>
+            <field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
+            <field name="arch" type="xml">
+                <field name="name" position="replace">
+                    <field name="name" readonly="1"/>
+                </field>
+            </field>
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added directory 'hr_contract_state'
=== added file 'hr_contract_state/__init__.py'
--- hr_contract_state/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_contract_state/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_contract

=== added file 'hr_contract_state/__openerp__.py'
--- hr_contract_state/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_contract_state/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,54 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Manage Employee Contracts',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Employee Contract Workflow and Notifications
+============================================
+
+Easily find and keep track of employees who are nearing the end of their contracts and
+trial periods.
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr_contract',
+        'hr_contract_init',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'hr_contract_cron.xml',
+        'hr_contract_data.xml',
+        'hr_contract_workflow.xml',
+        'hr_contract_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_contract_state/hr_contract.py'
--- hr_contract_state/hr_contract.py	1970-01-01 00:00:00 +0000
+++ hr_contract_state/hr_contract.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,247 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import time
+
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+
+import netsvc
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+from openerp.osv import fields, osv
+
+
+class hr_contract(osv.osv):
+
+    _name = 'hr.contract'
+    _inherit = ['hr.contract', 'mail.thread', 'ir.needaction_mixin']
+
+    def _get_ids_from_employee(self, cr, uid, ids, context=None):
+
+        res = []
+        for ee in self.pool.get('hr.employee').browse(cr, uid, ids, context=context):
+            for contract in ee.contract_ids:
+                if contract.state not in ['pending_done', 'done']:
+                    res.append(contract.id)
+        return res
+
+    def _get_department(self, cr, uid, ids, field_name, arg, context=None):
+
+        res = dict.fromkeys(ids, False)
+        for contract in self.browse(cr, uid, ids, context=context):
+            if contract.department_id and contract.state in ['pending_done', 'done']:
+                res[contract.id] = contract.department_id.id
+            elif contract.employee_id.department_id:
+                res[contract.id] = contract.employee_id.department_id.id
+        return res
+
+    _columns = {
+        'state': fields.selection([('draft', 'Draft'),
+                                   ('trial', 'Trial'),
+                                   ('trial_ending', 'Trial Period Ending'),
+                                   ('open', 'Open'),
+                                   ('contract_ending', 'Ending'),
+                                   ('pending_done', 'Pending Termination'),
+                                   ('done', 'Completed')],
+                                  'State',
+                                  readonly=True),
+
+        # store this field in the database and trigger a change only if the contract is
+        # in the right state: we don't want future changes to an employee's department to
+        # impact past contracts that have now ended. Increased priority to
+        # override hr_simplify.
+        'department_id': fields.function(_get_department, type='many2one', method=True,
+                                         obj='hr.department', string="Department", readonly=True,
+                                         store={
+                                             'hr.employee': (_get_ids_from_employee, ['department_id'], 10)}),
+
+        # At contract end this field will hold the job_id, and the
+        # job_id field will be set to null so that modules that
+        # reference job_id don't include deactivated employees.
+        'end_job_id': fields.many2one('hr.job', 'Job Title', readonly=True),
+
+        # The following are redefined again to make them editable only in
+        # certain states
+        'employee_id': fields.many2one('hr.employee', "Employee", required=True, readonly=True,
+                                       states={
+                                           'draft': [('readonly', False)]}),
+        'type_id': fields.many2one('hr.contract.type', "Contract Type", required=True, readonly=True,
+                                   states={'draft': [('readonly', False)]}),
+        'date_start': fields.date('Start Date', required=True, readonly=True,
+                                  states={'draft': [('readonly', False)]}),
+        'wage': fields.float('Wage', digits=(16, 2), required=True, readonly=True,
+                             states={'draft': [('readonly', False)]},
+                             help="Basic Salary of the employee"),
+    }
+
+    _defaults = {
+        'state': 'draft',
+    }
+
+    _track = {
+        'state': {
+            'hr_contract_state.mt_alert_trial_ending': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'trial_ending',
+            'hr_contract_state.mt_alert_open': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open',
+            'hr_contract_state.mt_alert_contract_ending': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'contract_ending',
+        },
+    }
+
+    def _needaction_domain_get(self, cr, uid, context=None):
+
+        users_obj = self.pool.get('res.users')
+        domain = []
+
+        if users_obj.has_group(cr, uid, 'base.group_hr_manager'):
+            domain = [
+                ('state', 'in', ['draft', 'contract_ending', 'trial_ending'])]
+            return domain
+
+        return False
+
+    def onchange_job(self, cr, uid, ids, job_id, context=None):
+
+        import logging
+        _l = logging.getLogger(__name__)
+        _l.warning('hr_contract_state: onchange_job()')
+        res = False
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+        if ids:
+            contract = self.browse(cr, uid, ids[0], context=None)
+            if contract.state != 'draft':
+                return res
+        return super(hr_contract, self).onchange_job(cr, uid, ids, job_id, context=context)
+
+    def condition_trial_period(self, cr, uid, ids, context=None):
+
+        for contract in self.browse(cr, uid, ids, context=context):
+            if not contract.trial_date_start:
+                return False
+        return True
+
+    def try_signal_ending_contract(self, cr, uid, context=None):
+
+        d = datetime.now().date() + relativedelta(days=+30)
+        ids = self.search(cr, uid, [
+            ('state', '=', 'open'),
+            ('date_end', '<=', d.strftime(
+                DEFAULT_SERVER_DATE_FORMAT))
+        ], context=context)
+        if len(ids) == 0:
+            return
+
+        wkf = netsvc.LocalService('workflow')
+        [wkf.trg_validate(uid, 'hr.contract', contract.id, 'signal_ending_contract', cr)
+         for contract in self.browse(cr, uid, ids, context=context)]
+
+        return
+
+    def try_signal_contract_completed(self, cr, uid, context=None):
+
+        d = datetime.now().date()
+        ids = self.search(cr, uid, [
+            ('state', '=', 'open'),
+            ('date_end', '<', d.strftime(
+                DEFAULT_SERVER_DATE_FORMAT))
+        ], context=context)
+        if len(ids) == 0:
+            return
+
+        wkf = netsvc.LocalService('workflow')
+        [wkf.trg_validate(uid, 'hr.contract', contract.id, 'signal_pending_done', cr)
+         for contract in self.browse(cr, uid, ids, context=context)]
+
+        return
+
+    def try_signal_ending_trial(self, cr, uid, context=None):
+
+        d = datetime.now().date() + relativedelta(days=+10)
+        ids = self.search(cr, uid, [
+            ('state', '=', 'trial'),
+            ('trial_date_end', '<=', d.strftime(
+                DEFAULT_SERVER_DATE_FORMAT))
+        ], context=context)
+        if len(ids) == 0:
+            return
+
+        wkf = netsvc.LocalService('workflow')
+        [wkf.trg_validate(uid, 'hr.contract', contract.id, 'signal_ending_trial', cr)
+         for contract in self.browse(cr, uid, ids, context=context)]
+
+        return
+
+    def try_signal_open(self, cr, uid, context=None):
+
+        d = datetime.now().date() + relativedelta(days=-5)
+        ids = self.search(cr, uid, [
+            ('state', '=', 'trial_ending'),
+            ('trial_date_end', '<=', d.strftime(
+                DEFAULT_SERVER_DATE_FORMAT))
+        ], context=context)
+        if len(ids) == 0:
+            return
+
+        wkf = netsvc.LocalService('workflow')
+        [wkf.trg_validate(uid, 'hr.contract', contract.id, 'signal_open', cr)
+         for contract in self.browse(cr, uid, ids, context=context)]
+
+        return
+
+    def onchange_start(self, cr, uid, ids, date_start, context=None):
+
+        res = {'value': {}}
+        res['value']['trial_date_start'] = date_start
+
+        return res
+
+    def state_trial(self, cr, uid, ids, context=None):
+
+        self.write(cr, uid, ids, {'state': 'trial'}, context=context)
+        return True
+
+    def state_open(self, cr, uid, ids, context=None):
+
+        self.write(cr, uid, ids, {'state': 'open'}, context=context)
+        return True
+
+    def state_pending_done(self, cr, uid, ids, context=None):
+
+        self.write(cr, uid, ids, {'state': 'pending_done'}, context=context)
+        return True
+
+    def state_done(self, cr, uid, ids, context=None):
+
+        for i in ids:
+            data = self.read(
+                cr, uid, i, ['date_end', 'job_id'], context=context)
+            vals = {'state': 'done',
+                    'date_end': False,
+                    'job_id': False,
+                    'end_job_id': data['job_id'][0]}
+
+            if data.get('date_end', False):
+                vals['date_end'] = data['date_end']
+            else:
+                vals['date_end'] = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
+            self.write(cr, uid, ids, vals, context=context)
+
+        return True

=== added file 'hr_contract_state/hr_contract_cron.xml'
--- hr_contract_state/hr_contract_cron.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_state/hr_contract_cron.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record model="ir.cron" id="hr_contract_ending_cron">
+            <field name="name">Contracts Nearing Completion</field>
+            <field name="interval_number">1</field>
+            <field name="interval_type">days</field>
+            <field name="numbercall">-1</field>
+            <field eval="(DateTime.now() + timedelta(days= +1)).strftime('%Y-%m-%d 2:30:00')" name="nextcall"/>
+            <field eval="True" name="doall"/>
+            <field eval="'hr.contract'" name="model"/>
+            <field eval="'try_signal_ending_contract'" name="function"/>
+            <field eval="'()'" name="args"/>
+        </record>
+        
+        <record model="ir.cron" id="hr_contract_completed_cron">
+            <field name="name">Contracts That Have Ended</field>
+            <field name="interval_number">1</field>
+            <field name="interval_type">days</field>
+            <field name="numbercall">-1</field>
+            <field eval="(DateTime.now() + timedelta(days= +1)).strftime('%Y-%m-%d 2:45:00')" name="nextcall"/>
+            <field eval="True" name="doall"/>
+            <field eval="'hr.contract'" name="model"/>
+            <field eval="'try_signal_contract_completed'" name="function"/>
+            <field eval="'()'" name="args"/>
+        </record>
+        
+        <record model="ir.cron" id="hr_trial_period_ending_cron">
+            <field name="name">Contracts Nearing the End of the Trial Period</field>
+            <field name="interval_number">1</field>
+            <field name="interval_type">days</field>
+            <field name="numbercall">-1</field>
+            <field eval="(DateTime.now() + timedelta(days= +1)).strftime('%Y-%m-%d 3:00:00')" name="nextcall"/>
+            <field eval="True" name="doall"/>
+            <field eval="'hr.contract'" name="model"/>
+            <field eval="'try_signal_ending_trial'" name="function"/>
+            <field eval="'()'" name="args"/>
+        </record>
+        
+        <record model="ir.cron" id="hr_trial_period_ended_cron">
+            <field name="name">Contracts Past the End of the Trial Period</field>
+            <field name="interval_number">1</field>
+            <field name="interval_type">days</field>
+            <field name="numbercall">-1</field>
+            <field eval="(DateTime.now() + timedelta(days= +1)).strftime('%Y-%m-%d 3:30:00')" name="nextcall"/>
+            <field eval="True" name="doall"/>
+            <field eval="'hr.contract'" name="model"/>
+            <field eval="'try_signal_open'" name="function"/>
+            <field eval="'()'" name="args"/>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_contract_state/hr_contract_data.xml'
--- hr_contract_state/hr_contract_data.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_state/hr_contract_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Alert-related subtypes for messaging / Chatter -->
+        
+        <record id="mt_alert_trial_ending" model="mail.message.subtype">
+            <field name="name">Trial Period Ending</field>
+            <field name="res_model">hr.contract</field>
+            <field name="description">Trial Period is ending</field>
+        </record>
+        
+        <record id="mt_alert_contract_ending" model="mail.message.subtype">
+            <field name="name">Contract Period Ending</field>
+            <field name="res_model">hr.contract</field>
+            <field name="description">Contract Period is ending</field>
+        </record>
+        
+        <record id="mt_alert_open" model="mail.message.subtype">
+            <field name="name">Contract Open</field>
+            <field name="res_model">hr.contract</field>
+            <field name="description">Contract is Open</field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_contract_state/hr_contract_view.xml'
--- hr_contract_state/hr_contract_view.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_state/hr_contract_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <!-- Menus -->
+        
+        <menuitem name="Contracts"
+            id="submenu_hr_contracts"
+            parent="hr.menu_hr_main"
+            sequence="100" groups="base.group_hr_user"/>
+        
+        <!-- Remove stock Contracts menuitem and put it in the submenu -->
+        <menuitem action="hr_contract.action_hr_contract" id="hr_contract.hr_menu_contract" parent="submenu_hr_contracts" name="Contracts" sequence="10" groups="base.group_hr_user"/>
+        
+        <record id="hr_contract_view_tree" model="ir.ui.view">
+            <field name="name">hr.contract.tree.state</field>
+            <field name="model">hr.contract</field>
+            <field name="inherit_id" ref="hr_contract.hr_contract_view_tree"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='employee_id']" position="after">
+                        <field name="department_id"/>
+                    </xpath>
+                    <xpath expr="//field[@name='date_end']" position="after">
+                        <field name="state"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+        <record id="view_expiring_contracts_tree" model="ir.ui.view">
+            <field name="name">hr.contract.contract.expire.tree</field>
+            <field name="model">hr.contract</field>
+            <field name="arch" type="xml">
+                <tree string="Expiring Conracts">
+                    <field name="employee_id"/>
+                    <field name="department_id"/>
+                    <field name="job_id"/>
+                    <field name="trial_date_end"/>
+                    <field name="date_end"/>
+                    <field name="state"/>
+                </tree>
+            </field>
+        </record>
+        <record id="open_expiring_contracts" model="ir.actions.act_window">
+            <field name="name">Ending Trials &amp; Contracts</field>
+            <field name="res_model">hr.contract</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="view_id" ref="view_expiring_contracts_tree"/>
+            <field name="domain">['|',('state','in',['contract_ending']),('state','in',['trial_ending'])]</field>
+            <field name="help" type="html">
+              <p>
+                There are currently no contracts or trial periods that are about to expire.
+              </p>
+            </field>
+        </record>
+        <menuitem action="open_expiring_contracts"
+                  id="menu_expiring_contracts"
+                  parent="submenu_hr_contracts"
+                  groups="base.group_hr_user"
+                  sequence="20"/>
+        
+        <record id="view_draft_contracts_tree" model="ir.ui.view">
+            <field name="name">hr.contract.contract.draft.tree</field>
+            <field name="model">hr.contract</field>
+            <field name="arch" type="xml">
+                <tree string="Contracts to be Approved">
+                    <field name="employee_id"/>
+                    <field name="department_id"/>
+                    <field name="job_id"/>
+                    <field name="date_start"/>
+                    <field name="trial_date_end"/>
+                    <field name="date_end"/>
+                    <field name="state"/>
+                </tree>
+            </field>
+        </record>
+        <record id="open_draft_contracts" model="ir.actions.act_window">
+            <field name="name">Contracts to be Approved</field>
+            <field name="res_model">hr.contract</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="view_id" ref="view_draft_contracts_tree"/>
+            <field name="domain">[('state','in',['draft'])]</field>
+            <field name="help" type="html">
+              <p>
+                There are currently no contracts that need to be approved.
+              </p>
+            </field>
+        </record>
+        <menuitem action="open_draft_contracts"
+                  id="menu_draft_contracts"
+                  parent="submenu_hr_contracts"
+                  groups="base.group_hr_user"
+                  sequence="30"/>
+       
+       <!-- HR Contract: Form View -->
+       <record id="view_contract_form" model="ir.ui.view">
+           <field name="name">hr.contract.form.inherit.state</field>
+           <field name="model">hr.contract</field>
+           <field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
+           <field name="arch" type="xml">
+               <data>
+                   <xpath expr="//div[@class='oe_title']" position="before">
+                       <header>
+                           <button name="signal_confirm" type="workflow" string="Confirm" 
+                                   groups="base.group_hr_manager" states="draft" class="oe_highlight"/>
+                           <button name="signal_open" type="workflow" string="Trial Successfull"
+                                   groups="base.group_hr_manager" states="trial_ending" class="oe_highlight"/>
+                           <button name="signal_done" type="workflow" string="End Contract"
+                                   groups="base.group_hr_manager" states="trial,trial_ending,open,contract_ending,pending_done" class="oe_highlight"/>
+                           <field name="state" widget="statusbar"/>
+                       </header>
+                   </xpath>
+                   <xpath expr="//field[@name='date_start']" position="replace">
+                       <field name="date_start" class="oe_inline" on_change="onchange_start(date_start)"/> -
+                   </xpath>
+                   <xpath expr="//field[@name='trial_date_end']" position="replace">
+                       <field name="trial_date_end" class="oe_inline" attrs="{'required':[('trial_date_start','!=',False)]}"/>
+                   </xpath>
+                   <xpath expr="//sheet" position="after">
+                        <div class="oe_chatter">
+                            <field name="message_follower_ids" widget="mail_followers"/>
+                            <field name="message_ids" widget="mail_thread"/>
+                        </div>
+                   </xpath>
+               </data>
+           </field>
+       </record>
+        
+       <record id="contract_view_init_form" model="ir.ui.view">
+            <field name="name">hr.contract.view.form.contract_init</field>
+            <field name="model">hr.contract</field>
+            <field name="inherit_id" ref="hr_contract_init.hr_contract_view_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='job_id']" position="replace">
+                    <field name="job_id" required="1" domain="[('department_id', '=', employee_dept_id)]" on_change="onchange_job(job_id)" attrs="{'invisible': [('state','=','done')]}"/>
+                    <field name="end_job_id" attrs="{'invisible': [('state','!=','done')]}"/>
+                </xpath>
+                <xpath expr="//field[@name='trial_date_start']" position="replace">
+                    <field name="trial_date_start" class="oe_inline" on_change="onchange_trial(trial_date_start)" attrs="{'required':[('trial_date_end','!=',False)]}"/> -
+                </xpath>
+            </field>
+        </record>
+        
+        <record id="hr_hr_employee_view_form2" model="ir.ui.view">
+            <field name="name">hr.employee.view.form.contract_state</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr_contract.hr_hr_employee_view_form2"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//button[@string='Contracts']" position="replace">
+                        <button name="%(hr_contract.act_hr_employee_2_hr_contract)d" string="Contracts" type="action" groups="base.group_hr_user"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_contract_state/hr_contract_workflow.xml'
--- hr_contract_state/hr_contract_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_contract_state/hr_contract_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Contract Workflow Definition -->
+        <record id="wkf_contract" model="workflow">
+            <field name="name">hr.contract.basic</field>
+            <field name="osv">hr.contract</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (Stages) -->
+        
+        <record id="act_draft" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract"/>
+            <field name="name">draft</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'draft'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_trial" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract"/>
+            <field name="name">trial</field>
+            <field name="kind">function</field>
+            <field name="action">state_trial()</field>
+        </record>
+        
+        <record id="act_trial_ending" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract"/>
+            <field name="name">trial_ending</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'trial_ending'})</field>
+        </record>
+        
+        <record id="act_open" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract"/>
+            <field name="name">open</field>
+            <field name="kind">function</field>
+            <field name="action">state_open()</field>
+        </record>
+        
+        <record id="act_contract_ending" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract"/>
+            <field name="name">contract_ending</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'contract_ending'})</field>
+        </record>
+        
+        <record id="act_pending_done" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract"/>
+            <field name="name">pending_done</field>
+            <field name="kind">function</field>
+            <field name="action">state_pending_done()</field>
+        </record>
+        
+        <record id="act_done" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_contract"/>
+            <field name="name">done</field>
+            <field name="kind">function</field>
+            <field name="action">state_done()</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="draft2trial" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_trial"/>
+            <field name="condition">condition_trial_period() == True</field>
+            <field name="signal">signal_confirm</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="draft2open" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_open"/>
+            <field name="condition">condition_trial_period() == False</field>
+            <field name="signal">signal_confirm</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="trial2end_trial" model="workflow.transition">
+            <field name="act_from" ref="act_trial"/>
+            <field name="act_to" ref="act_trial_ending"/>
+            <field name="signal">signal_ending_trial</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="trial2pending_done" model="workflow.transition">
+            <field name="act_from" ref="act_trial"/>
+            <field name="act_to" ref="act_pending_done"/>
+            <field name="signal">signal_pending_done</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="trial2done" model="workflow.transition">
+            <field name="act_from" ref="act_trial"/>
+            <field name="act_to" ref="act_done"/>
+            <field name="signal">signal_done</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="end_trial2open" model="workflow.transition">
+            <field name="act_from" ref="act_trial_ending"/>
+            <field name="act_to" ref="act_open"/>
+            <field name="signal">signal_open</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="end_trial2pending_done" model="workflow.transition">
+            <field name="act_from" ref="act_trial_ending"/>
+            <field name="act_to" ref="act_pending_done"/>
+            <field name="signal">signal_pending_done</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="end_trial2done" model="workflow.transition">
+            <field name="act_from" ref="act_trial_ending"/>
+            <field name="act_to" ref="act_done"/>
+            <field name="signal">signal_done</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="open2end_contract" model="workflow.transition">
+            <field name="act_from" ref="act_open"/>
+            <field name="act_to" ref="act_contract_ending"/>
+            <field name="signal">signal_ending_contract</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="open2pending_done" model="workflow.transition">
+            <field name="act_from" ref="act_open"/>
+            <field name="act_to" ref="act_pending_done"/>
+            <field name="signal">signal_pending_done</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="open2done" model="workflow.transition">
+            <field name="act_from" ref="act_open"/>
+            <field name="act_to" ref="act_done"/>
+            <field name="signal">signal_done</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="end_contract2pending_done" model="workflow.transition">
+            <field name="act_from" ref="act_contract_ending"/>
+            <field name="act_to" ref="act_pending_done"/>
+            <field name="signal">signal_pending_done</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="end_contract2done" model="workflow.transition">
+            <field name="act_from" ref="act_contract_ending"/>
+            <field name="act_to" ref="act_done"/>
+            <field name="signal">signal_done</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="pending_done2open" model="workflow.transition">
+            <field name="act_from" ref="act_pending_done"/>
+            <field name="act_to" ref="act_open"/>
+            <field name="signal">signal_open</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="pending_done2done" model="workflow.transition">
+            <field name="act_from" ref="act_pending_done"/>
+            <field name="act_to" ref="act_done"/>
+            <field name="signal">signal_done</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+    
+    </data>
+</openerp>

=== added directory 'hr_contract_state/security'
=== added file 'hr_contract_state/security/ir.model.access.csv'
--- hr_contract_state/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_contract_state/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_contract_user,access_hr_contract,model_hr_contract,base.group_hr_user,1,1,1,1
+access_hr_contract_type_user,access_hr_contract_type,hr_contract.model_hr_contract_type,base.group_hr_user,1,0,0,0

=== added directory 'hr_department_sequence'
=== added file 'hr_department_sequence/__init__.py'
--- hr_department_sequence/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_department_sequence/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,21 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyrigth (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>
+#
+#    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/>.
+#
+#
+
+from . import hr_department

=== added file 'hr_department_sequence/__openerp__.py'
--- hr_department_sequence/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_department_sequence/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,45 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyrigth (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>
+#
+#    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/>.
+#
+#
+
+{
+    'name': 'Department Sequence',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Order by Parent-Child Relationship and by Sequence Number
+=========================================================
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://www.openerp.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_department_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_department_sequence/hr_department.py'
--- hr_department_sequence/hr_department.py	1970-01-01 00:00:00 +0000
+++ hr_department_sequence/hr_department.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,39 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyrigth (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>
+#
+#    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/>.
+#
+#
+
+from openerp.osv import fields, osv
+
+
+class hr_department(osv.Model):
+
+    _name = 'hr.department'
+    _inherit = 'hr.department'
+
+    _columns = {
+        'code': fields.char('Code', size=64),
+        'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of departments."),
+        'parent_left': fields.integer('Left Parent', select=1),
+        'parent_right': fields.integer('Right Parent', select=1),
+    }
+
+    _parent_name = "parent_id"
+    _parent_store = True
+    _parent_order = 'sequence, name'
+    _order = 'parent_left'

=== added file 'hr_department_sequence/hr_department_view.xml'
--- hr_department_sequence/hr_department_view.xml	1970-01-01 00:00:00 +0000
+++ hr_department_sequence/hr_department_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="hr_department_view" model="ir.ui.view">
+            <field name="name">hr.department.form.sequence</field>
+            <field name="model">hr.department</field>
+            <field name="inherit_id" ref="hr.view_department_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='name']" position="after">
+                    <field name="code"/>
+                </xpath>
+                <xpath expr="//field[@name='company_id']" position="after">
+                    <field name="sequence"/>
+                </xpath>
+            </field>
+        </record>
+        
+        <record id="hr_department_tree_view" model="ir.ui.view">
+            <field name="name">hr.department.tree.sequence</field>
+            <field name="model">hr.department</field>
+            <field name="inherit_id" ref="hr.view_department_tree"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='complete_name']" position="after">
+                    <field name="code"/>
+                    <field name="sequence"/>
+                </xpath>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_emergency_contact'
=== added file 'hr_emergency_contact/__init__.py'
--- hr_emergency_contact/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_emergency_contact/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr

=== added file 'hr_emergency_contact/__openerp__.py'
--- hr_emergency_contact/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_emergency_contact/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,46 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'HR Emergency Contact',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Emergency Contact information for Employee
+==========================================
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://www.openerp.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_emergency_contact/emergency_contact.py'
--- hr_emergency_contact/emergency_contact.py	1970-01-01 00:00:00 +0000
+++ hr_emergency_contact/emergency_contact.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,41 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from osv import fields, osv
+
+
+class hr_employee(osv.osv):
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        'ec_name': fields.char('Name', size=256, required=True),
+        'ec_relationship': fields.char('Relationship', size=128, required=True),
+        'ec_tel1': fields.char('Primary Phone No.', size=32),
+        'ec_tel2': fields.char('Secondary Phone No.', size=32),
+        'ec_woreda': fields.char('Subcity/Woreda', size=32),
+        'ec_kebele': fields.char('Kebele', size=8),
+        'ec_houseno': fields.char('House No.', size=8),
+        'ec_address': fields.char('Address 2', size=256),
+        'ec_country_id': fields.many2one('res.country', 'Country'),
+        'ec_state_id': fields.many2one('res.country.state', 'State', domain="[('country_id','=',country_id)]"),
+    }

=== added file 'hr_emergency_contact/emergency_contact_view.xml'
--- hr_emergency_contact/emergency_contact_view.xml	1970-01-01 00:00:00 +0000
+++ hr_emergency_contact/emergency_contact_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+##############################################################################
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+-->
+
+<openerp>
+    <data>
+        
+        <record id="emergency_contact_view_form" model="ir.ui.view">
+            <field name="name">hr.emergency_contact.form</field>
+            <field name="model">hr.emergency_contact</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="Emergency Contact">
+                </form>
+            </field>
+        </record>
+        
+        <record id="hr_employee_view_form" model="ir.ui.view">
+            <field name="name">hr.employee.view.form.inherit</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <xpath expr='//form/notebook/page[@string="Personal Information"]' position="after">
+                    <page string="Emergency Contact">
+                        <field name="ec_name"/>
+                        <field name="ec_relationship"/>
+                        <field name="ec_tel1"/>
+                        <field name="ec_tel2"/>
+                        <group colspan="4" col="6">
+                            <field name="ec_woreda"/>
+                            <field name="ec_kebele"/>
+                            <field name="ec_houseno"/>
+                        </group>
+                        <field name="ec_address" colspan="4"/>
+                        <field name="ec_country_id"/>
+                        <field name="ec_state_id"/>
+                    </page>
+                </xpath>
+            </field>
+        </record>
+        
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'hr_emergency_contact/hr.py'
--- hr_emergency_contact/hr.py	1970-01-01 00:00:00 +0000
+++ hr_emergency_contact/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,50 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from osv import fields, osv
+
+
+class hr_employee(osv.osv):
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        'ec_name': fields.char('Name', size=256),
+        'ec_relationship': fields.char('Relationship', size=64),
+        'ec_tel1': fields.char('Primary Phone No.', size=32),
+        'ec_tel2': fields.char('Secondary Phone No.', size=32),
+        'ec_woreda': fields.char('Subcity/Woreda', size=32),
+        'ec_kebele': fields.char('Kebele', size=8),
+        'ec_houseno': fields.char('House No.', size=8),
+        'ec_address': fields.char('Address 2', size=256),
+        'ec_country_id': fields.many2one('res.country', 'Country'),
+        'ec_state_id': fields.many2one('res.country.state', 'State', domain="[('country_id','=',country_id)]"),
+    }
+
+    def _get_country(self, cr, uid, context=None):
+        cid = self.pool.get('res.country').search(
+            cr, uid, [('code', '=', 'ET')], context=context)
+        return cid[0]
+
+    _defaults = {
+        'ec_country_id': _get_country,
+    }

=== added file 'hr_emergency_contact/hr_view.xml'
--- hr_emergency_contact/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_emergency_contact/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+##############################################################################
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+-->
+
+<openerp>
+    <data>
+        
+        <record id="hr_employee_view_form" model="ir.ui.view">
+            <field name="name">hr.employee.view.form.inherit</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <xpath expr='//page[@string="Personal Information"]' position="after">
+                    <page string="Emergency Contact">
+                        <group>
+                            <group string="Details">
+                                <field name="ec_name"/>
+                                <field name="ec_relationship"/>
+                                <field name="ec_tel1"/>
+                                <field name="ec_tel2"/>
+                            </group>
+                            <group string="Address">
+                                <field name="ec_woreda"/>
+                                <field name="ec_kebele"/>
+                                <field name="ec_houseno"/>
+                                <field name="ec_address"/>
+                                <field name="ec_country_id"/>
+                                <field name="ec_state_id"/>
+                            </group>
+                        </group>
+                    </page>
+                </xpath>
+            </field>
+        </record>
+        
+    </data>
+</openerp>
\ No newline at end of file

=== added directory 'hr_employee_education'
=== added file 'hr_employee_education/__init__.py'
--- hr_employee_education/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_education/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,23 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr
+from . import wizard

=== added file 'hr_employee_education/__openerp__.py'
--- hr_employee_education/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_education/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,49 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Employee Education Records',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Details About and Employee's Education
+======================================
+
+    Add an extra field about an employee's education.
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://www.openerp.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'wizard/hr_employee_by_department_view.xml',
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_employee_education/hr.py'
--- hr_employee_education/hr.py	1970-01-01 00:00:00 +0000
+++ hr_employee_education/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,55 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from datetime import datetime
+
+from osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DFORMAT
+
+EDUCATION_SELECTION = [
+    ('none', 'No Education'),
+    ('primary', 'Primary School'),
+    ('secondary', 'Secondary School'),
+    ('diploma', 'Diploma'),
+    ('degree1', 'First Degree'),
+    ('masters', 'Masters Degree'),
+    ('phd', 'PhD'),
+]
+
+
+class hr_employee(osv.osv):
+
+    _inherit = 'hr.employee'
+
+    def _calculate_age(self, cr, uid, ids, field_name, arg, context=None):
+
+        res = dict.fromkeys(ids, False)
+        for ee in self.browse(cr, uid, ids, context=context):
+            if ee.birthday:
+                dBday = datetime.strptime(ee.birthday, OE_DFORMAT).date()
+                dToday = datetime.now().date()
+                res[ee.id] = (dToday - dBday).days / 365
+        return res
+
+    _columns = {
+        'education': fields.selection(EDUCATION_SELECTION, 'Education'),
+        'age': fields.function(_calculate_age, type='integer', method=True, string='Age'),
+    }

=== added file 'hr_employee_education/hr_view.xml'
--- hr_employee_education/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_education/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+##############################################################################
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+-->
+
+<openerp>
+    <data>
+        
+        <record id="hr_employee_view_form" model="ir.ui.view">
+            <field name="name">hr.employee.view.form.inherit.education</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//group[@string='Birth']" position="after">
+                        <group string="Education">
+                            <field name="education"/>
+                        </group>
+                    </xpath>
+                    <xpath expr="//field[@name='birthday']" position="after">
+                        <field name="age"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+        <!-- Employee Personal/Education View -->
+
+        <record id="view_employee_social_tree" model="ir.ui.view">
+            <field name="name">hr.employee.tree</field>
+            <field name="model">hr.employee</field>
+            <field name="arch" type="xml">
+                <tree string="Employees">
+                    <field name="name"/>
+                    <field name="age"/>
+                    <field name="gender"/>
+                    <field name="education"/>
+                    <field name="marital"/>
+                    <field name="company_id" groups="base.group_multi_company"/>
+                    <field name="department_id"/>
+                    <field name="job_id"/>
+                    <field name="parent_id" invisible="1"/>
+                    <field name="coach_id" invisible="1"/>
+                </tree>
+            </field>
+        </record>
+
+        <record model="ir.actions.act_window" id="open_hr_employee_social_welfare">
+            <field name="name">Employee Social Welfare Report</field>
+            <field name="res_model">hr.employee</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="view_id" eval="view_employee_social_tree"/>
+            <field name="search_view_id" ref="hr.view_employee_filter"/>
+        </record>
+        <menuitem name="Employee Social Welfare Report"
+            parent="hr.menu_hr_reporting"
+            id="menu_open_hr_employee_social_welfare"
+            action="open_hr_employee_social_welfare"
+            sequence="40"/>
+        
+    </data>
+</openerp>
\ No newline at end of file

=== added directory 'hr_employee_education/wizard'
=== added file 'hr_employee_education/wizard/__init__.py'
--- hr_employee_education/wizard/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_education/wizard/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_employee_by_department

=== added file 'hr_employee_education/wizard/hr_employee_by_department.py'
--- hr_employee_education/wizard/hr_employee_by_department.py	1970-01-01 00:00:00 +0000
+++ hr_employee_education/wizard/hr_employee_by_department.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,95 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp.osv import fields, osv
+
+from hr_employee_education.hr import EDUCATION_SELECTION
+
+import logging
+_l = logging.getLogger(__name__)
+
+
+class hr_al(osv.TransientModel):
+
+    _name = 'hr.employee.edu'
+
+    _columns = {
+        'department_id': fields.many2one('hr.department', 'Department'),
+        'line_ids': fields.one2many('hr.employee.edu.line', 'edu_id', 'Employee Education Lines'),
+    }
+
+    def _get_lines(self, cr, uid, context=None):
+
+        if context == None:
+            context = {}
+
+        res = []
+        ee_obj = self.pool.get('hr.employee')
+        department_id = context.get('active_id', False)
+        ee_ids = ee_obj.search(cr, uid, [('department_id', '=', department_id),
+                                         ('active', '=', True)], context=context)
+        data = ee_obj.read(cr, uid, ee_ids, ['education'], context=context)
+        for record in data:
+            vals = {
+                'employee_id': record['id'],
+                'days': record['education'],
+            }
+
+            res.append(vals)
+        return res
+
+    def _get_department(self, cr, uid, context=None):
+
+        if context == None:
+            context = {}
+        department_id = context.get('active_id', False)
+        return department_id
+
+    _defaults = {
+        'department_id': _get_department,
+        'line_ids': _get_lines,
+    }
+
+    def add_records(self, cr, uid, ids, context=None):
+
+        e_obj = self.pool.get('hr.employee')
+        data = self.read(cr, uid, ids[0], ['line_ids'], context=context)
+        for line in self.pool.get(
+            'hr.employee.edu.line').browse(cr, uid, data['line_ids'],
+                                           context=context):
+            if line.education:
+                e_obj.write(
+                    cr, uid, line.employee_id.id, {
+                        'education': line.education},
+                    context=context)
+
+        return {'type': 'ir.actions.act_window_close'}
+
+
+class hr_edu_line(osv.TransientModel):
+
+    _name = 'hr.employee.edu.line'
+
+    _columns = {
+        'edu_id': fields.many2one('hr.employee.edu', 'Education by Dept.'),
+        'employee_id': fields.many2one('hr.employee', 'Employee', required=True),
+        'education': fields.selection(EDUCATION_SELECTION, 'Education'),
+    }

=== added file 'hr_employee_education/wizard/hr_employee_by_department_view.xml'
--- hr_employee_education/wizard/hr_employee_by_department_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_education/wizard/hr_employee_by_department_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="view_edu_form" model="ir.ui.view">
+            <field name="name">hr.employee.edu.form</field>
+            <field name="model">hr.employee.edu</field>
+            <field name="arch" type="xml">
+                <form string="Employee Education by Department" version="7.0">
+                    <header>
+                        <button name="add_records" type="object" string="Save" class="oe_highlight"/>
+                    </header>
+                    <div>
+                        <h2>
+                            <field name="department_id" readonly="1"/>
+                        </h2>
+                    </div>
+                    <group>
+                        <group string="Employee Lines">
+                            <field name="line_ids" nolabel="1"/>
+                        </group>
+                        <group></group>
+                    </group>
+                </form>
+            </field>
+        </record>
+        
+        <record id="view_edu_line_tree" model="ir.ui.view">
+            <field name="name">hr.employee.edu.line.tree</field>
+            <field name="model">hr.employee.edu.line</field>
+            <field name="arch" type="xml">
+                <tree string="Employee Lines" editable="top">
+                    <field name="employee_id"/>
+                    <field name="education"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_edu_line_form" model="ir.ui.view">
+            <field name="name">hr.employee.edu.line.form</field>
+            <field name="model">hr.employee.edu.line</field>
+            <field name="arch" type="xml">
+                <form string="Employee Education Line">
+                    <field name="employee_id"/>
+                    <field name="education"/>
+                </form>
+            </field>
+        </record>
+
+        <act_window id="action_hr_employee_education_by_department"
+                name="Employee Education Level"
+                res_model="hr.employee.edu" 
+                src_model="hr.department"
+                view_mode="form" 
+                key2="client_action_multi"
+                target="new"
+        />
+        
+    </data>
+</openerp>

=== added directory 'hr_employee_id'
=== added file 'hr_employee_id/__init__.py'
--- hr_employee_id/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_id/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_employee_id

=== added file 'hr_employee_id/__openerp__.py'
--- hr_employee_id/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_id/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,47 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Employee ID',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Employee Identification Numbers
+===============================
+Company wide unique employee ID
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_employee_id/hr_contract.py'
--- hr_employee_id/hr_contract.py	1970-01-01 00:00:00 +0000
+++ hr_employee_id/hr_contract.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,40 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from osv import fields, osv
+
+
+class hr_contract(osv.osv):
+
+    _inherit = 'hr.contract'
+
+    _columns = {
+        'name': fields.char('Contract Reference', size=32, required=False, readonly=True),
+    }
+
+    def create(self, cr, uid, vals, context=None):
+
+        cid = super(hr_contract, self).create(cr, uid, vals, context)
+        if cid:
+            ref = self.pool.get('ir.sequence').get(cr, uid, 'contract.ref')
+            self.pool.get('hr.contract').write(
+                cr, uid, cid, {'name': ref}, context=context)
+        return cid

=== added file 'hr_employee_id/hr_contract_sequence.xml'
--- hr_employee_id/hr_contract_sequence.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_id/hr_contract_sequence.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        <record id="seq_type_contract_ref" model="ir.sequence.type">
+            <field name="name">Contract Reference</field>
+            <field name="code">contract.ref</field>
+        </record>
+        <record id="seq_contract_ref" model="ir.sequence">
+            <field name="name">Contract Reference</field>
+            <field name="code">contract.ref</field>
+            <field name="prefix">HR/EC/%(year)s/</field>
+            <field name="padding">5</field>
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'hr_employee_id/hr_contract_view.xml'
--- hr_employee_id/hr_contract_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_id/hr_contract_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="hr_contract_view" model="ir.ui.view">
+            <field name="name">hr.contract.form.view.inherit.ref</field>
+            <field name="model">hr.contract</field>
+            <field name="type">form</field>
+            <field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
+            <field name="arch" type="xml">
+                <field name="name" position="replace">
+                    <field name="name" readonly="1"/>
+                </field>
+            </field>
+        </record>
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'hr_employee_id/hr_employee_id.py'
--- hr_employee_id/hr_employee_id.py	1970-01-01 00:00:00 +0000
+++ hr_employee_id/hr_employee_id.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,93 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import random
+import string
+from osv import fields, osv
+from tools.translate import _
+
+
+class hr_employee(osv.osv):
+
+    """Implement company wide unique identification number."""
+
+    IDLEN = 8
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        'employee_no': fields.char('Employee ID',
+                                   size=IDLEN,
+                                   readonly=True),
+        # Formatted version of employee ID
+        'f_employee_no': fields.char('Employee ID',
+                                     size=IDLEN + 2,
+                                     readonly=True),
+        'tin_no': fields.char('TIN No', size=10),
+    }
+
+    _sql_constraints = [
+        ('employeeno_uniq', 'unique(employee_no)',
+         'The Employee Number must be unique accross the company(s).'),
+        ('tinno_uniq', 'unique(tin_no)',
+         'There is already another employee with this TIN number.'),
+    ]
+
+    def _check_identification(self, cr, uid, ids, context=None):
+        obj = self.browse(cr, uid, ids[0], context=context)
+        if obj.identification_id or obj.tin_no:
+            return True
+        return False
+
+#    _constraints = [
+#        (_check_identification, 'At least one of the identification fields must be filled in.', ['identification_id', 'tin_no']),
+#    ]
+
+    def _generate_employeeno(self, cr, uid, arg):
+        """Generate a random employee identifacation number"""
+
+        tries = 0
+        max_tries = 50
+        while tries < max_tries:
+            rnd = random.SystemRandom()
+            eid = ''.join(rnd.choice(string.digits)
+                          for _ in xrange(self.IDLEN))
+            cr.execute(
+                '''SELECT employee_no FROM hr_employee WHERE employee_no=%s''', tuple((eid,)))
+            res = cr.fetchall()
+            if len(res) == 0:
+                break
+
+            tries += 1
+
+        if tries == max_tries:
+            raise osv.except_osv(
+                _('Error'), _('Unable to generate an Employee ID number that is unique.'))
+
+        return eid
+
+    def create(self, cr, uid, vals, context={}):
+
+        eid = self._generate_employeeno(cr, uid, context)
+        vals['employee_no'] = eid
+        vals['f_employee_no'] = '%s-%s-%s' % (eid[:2], eid[2:4], eid[4:])
+        return super(hr_employee, self).create(cr, uid, vals, context)

=== added file 'hr_employee_id/hr_payroll_view.xml'
--- hr_employee_id/hr_payroll_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_id/hr_payroll_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+##############################################################################
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+    Document   : hr_payroll_view.xml
+    Created on : April 15, 2011, 11:49 AM
+    Author     : mtm
+    Description:
+        Update employee record view to show Employee ID.
+-->
+
+<openerp>
+    <data>
+        <record id="view_employee_form" model="ir.ui.view">
+            <field name="name">hr.employee.form.inherit</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <field name="department_id" position="before">
+                    <field name="employee_no"/>
+                </field>
+            </field>
+        </record>
+    </data>
+</openerp>

=== added file 'hr_employee_id/hr_view.xml'
--- hr_employee_id/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_id/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+    <data>
+
+        <record id="view_employee_form" model="ir.ui.view">
+            <field name="name">hr.employee.form.inherit</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <div name="button_box" position="after">
+                        <div class="oe_title">
+                            <label for="f_employee_no" class="oe_edit_only"/>
+                            <h2>
+                                <field name="f_employee_no"/>
+                            </h2>
+                        </div>
+                    </div>
+                    <field name="identification_id" position="after">
+                        <field name="tin_no"/>
+                    </field>
+                </data>
+            </field>
+        </record>
+
+        <record id="view_employee_tree" model="ir.ui.view">
+            <field name="name">hr.employee.tree.inherit</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_tree"/>
+            <field name="arch" type="xml">
+                <data>
+                    <field name="name" position="after">
+                        <field name="employee_no" invisible="1"/>
+                        <field name="f_employee_no"/>
+                        <field name="tin_no"/>
+                    </field>
+                </data>
+            </field>
+        </record>
+
+        <record id="view_employee_filter" model="ir.ui.view">
+            <field name="name">Employees</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_filter"/>
+            <field name="arch" type="xml">
+                <field name="name" position="after">
+                    <field name="employee_no"/>
+                    <field name="tin_no"/>
+                </field>
+            </field>
+        </record>
+        <record model="ir.ui.view" id="hr_kanban_view_employees">
+            <field name="name">HR - Employess Kanban</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.hr_kanban_view_employees"/>
+            <field name="arch" type="xml">
+                <field name="job_id" position="before">
+                    <li><field name="f_employee_no"/></li>
+                </field>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_employee_legacy_id'
=== added file 'hr_employee_legacy_id/__init__.py'
--- hr_employee_legacy_id/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_legacy_id/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr

=== added file 'hr_employee_legacy_id/__openerp__.py'
--- hr_employee_legacy_id/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_legacy_id/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,49 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Legacy Employee ID',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Employee Identification Numbers from Previous System
+====================================================
+
+When switching from a legacy HR system to a new one. It may be necessary
+to have a record of the ID No. used in the previous system.
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_employee_legacy_id/hr.py'
--- hr_employee_legacy_id/hr.py	1970-01-01 00:00:00 +0000
+++ hr_employee_legacy_id/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,32 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp.osv import fields, osv
+
+
+class hr_employee(osv.Model):
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        'legacy_no': fields.char('Legacy ID', size=16),
+    }

=== added file 'hr_employee_legacy_id/hr_view.xml'
--- hr_employee_legacy_id/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_legacy_id/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+    <data>
+
+        <record id="view_employee_form" model="ir.ui.view">
+            <field name="name">hr.employee.form.inherit</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <field name="active" position="after">
+                        <field name="legacy_no"/>
+                    </field>
+                </data>
+            </field>
+        </record>
+
+        <record id="view_employee_tree" model="ir.ui.view">
+            <field name="name">hr.employee.tree.inherit</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_tree"/>
+            <field name="arch" type="xml">
+                <data>
+                    <field name="name" position="after">
+                        <field name="legacy_no" invisible="1"/>
+                    </field>
+                </data>
+            </field>
+        </record>
+
+        <record id="view_employee_filter" model="ir.ui.view">
+            <field name="name">Employees</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_filter"/>
+            <field name="arch" type="xml">
+                <field name="name" position="after">
+                    <field name="legacy_no"/>
+                </field>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_employee_seniority'
=== added file 'hr_employee_seniority/__init__.py'
--- hr_employee_seniority/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_seniority/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr

=== added file 'hr_employee_seniority/__openerp__.py'
--- hr_employee_seniority/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_seniority/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,47 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Employee Seniority',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Keep Track of Length of Employment
+==================================
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+        'hr_security',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_employee_seniority/hr.py'
--- hr_employee_seniority/hr.py	1970-01-01 00:00:00 +0000
+++ hr_employee_seniority/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,153 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from datetime import datetime, date, timedelta
+from dateutil.relativedelta import relativedelta
+
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DATEFORMAT
+
+
+class hr_employee(osv.Model):
+
+    _inherit = 'hr.employee'
+
+    def _get_contracts_list(self, employee):
+        '''Return list of contracts in chronological order'''
+
+        contracts = []
+        for c in employee.contract_ids:
+            l = len(contracts)
+            if l == 0:
+                contracts.append(c)
+            else:
+                dCStart = datetime.strptime(c.date_start, OE_DATEFORMAT).date()
+                i = l - 1
+                while i >= 0:
+                    dContractStart = datetime.strptime(
+                        contracts[i].date_start, OE_DATEFORMAT).date()
+                    if dContractStart < dCStart:
+                        contracts = contracts[:i + 1] + [c] + contracts[i + 1:]
+                        break
+                    elif i == 0:
+                        contracts = [c] + contracts
+                    i -= 1
+
+        return contracts
+
+    def _get_days_in_month(self, d):
+
+        last_date = d - timedelta(days=(d.day - 1)) + relativedelta(
+            months= +1) + relativedelta(days= -1)
+        return last_date.day
+
+    def get_months_service_to_date(self, cr, uid, ids, dToday=None, context=None):
+        '''Returns a dictionary of floats. The key is the employee id, and the value is
+        number of months of employment.'''
+
+        res = dict.fromkeys(ids, 0)
+        if dToday == None:
+            dToday = date.today()
+
+        for ee in self.pool.get('hr.employee').browse(cr, uid, ids, context=context):
+
+            delta = relativedelta(dToday, dToday)
+            contracts = self._get_contracts_list(ee)
+            if len(contracts) == 0:
+                res[ee.id] = (0.0, False)
+                continue
+
+            dInitial = datetime.strptime(
+                contracts[0].date_start, OE_DATEFORMAT).date()
+
+            if ee.initial_employment_date:
+                dFirstContract = dInitial
+                dInitial = datetime.strptime(
+                    ee.initial_employment_date, '%Y-%m-%d').date()
+                if dFirstContract < dInitial:
+                    raise osv.except_osv(_('Employment Date mismatch!'),
+                                         _('The initial employment date cannot be after the first contract in the system.\nEmployee: %s', ee.name))
+
+                delta = relativedelta(dFirstContract, dInitial)
+
+            for c in contracts:
+                dStart = datetime.strptime(c.date_start, '%Y-%m-%d').date()
+                if dStart >= dToday:
+                    continue
+
+                # If the contract doesn't have an end date, use today's date
+                # If the contract has finished consider the entire duration of
+                # the contract, otherwise consider only the months in the
+                # contract until today.
+                #
+                if c.date_end:
+                    dEnd = datetime.strptime(c.date_end, '%Y-%m-%d').date()
+                else:
+                    dEnd = dToday
+                if dEnd > dToday:
+                    dEnd = dToday
+
+                delta += relativedelta(dEnd, dStart)
+
+            # Set the number of months the employee has worked
+            date_part = float(delta.days) / float(
+                self._get_days_in_month(dInitial))
+            res[ee.id] = (
+                float((delta.years * 12) + delta.months) + date_part, dInitial)
+
+        return res
+
+    def _get_employed_months(self, cr, uid, ids, field_name, arg, context=None):
+
+        res = dict.fromkeys(ids, 0.0)
+        _res = self.get_months_service_to_date(cr, uid, ids, context=context)
+        for k, v in _res.iteritems():
+            res[k] = v[0]
+        return res
+
+    def _search_amount(self, cr, uid, obj, name, args, context):
+        ids = set()
+        for cond in args:
+            amount = cond[2]
+            if isinstance(cond[2], (list, tuple)):
+                if cond[1] in ['in', 'not in']:
+                    amount = tuple(cond[2])
+                else:
+                    continue
+            else:
+                if cond[1] in ['=like', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in', 'child_of']:
+                    continue
+
+            cr.execute("select id from hr_employee having %s %%s" %
+                       (cond[1]), (amount,))
+            res_ids = set(id[0] for id in cr.fetchall())
+            ids = ids and (ids & res_ids) or res_ids
+        if ids:
+            return [('id', 'in', tuple(ids))]
+        return [('id', '=', '0')]
+
+    _columns = {
+        'initial_employment_date': fields.date('Initial Date of Employment', groups=False,
+                                               help='Date of first employment if it was before the start of the first contract in the system.'),
+        'length_of_service': fields.function(_get_employed_months, type='float', method=True,
+                                             groups=False,
+                                             string='Lenght of Service'),
+    }

=== added file 'hr_employee_seniority/hr_view.xml'
--- hr_employee_seniority/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_seniority/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        
+        <!-- Employee Records -->
+        
+        <record id="hr_employee_view_tree" model="ir.ui.view">
+            <field name="name">hr.employee.tree.seniority</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_tree"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='work_phone']" position="before">
+                        <field name="length_of_service"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+        <record id="hr_employee_view_form_inherit" model="ir.ui.view">
+            <field name="name">hr.employee.form.seniority</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//group[@name='active_group']" position="after">
+                        <group string="Duration of Service">
+                            <field name="initial_employment_date"/>
+                            <field name="length_of_service"/>
+                        </group>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_employee_state'
=== added file 'hr_employee_state/__init__.py'
--- hr_employee_state/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_state/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,23 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr
+from . import wizard

=== added file 'hr_employee_state/__openerp__.py'
--- hr_employee_state/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_state/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,54 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Employment Status',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Employee's Employment Status
+============================
+
+Track the HR status of employees.
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+        'hr_contract_state',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'wizard/end_contract_view.xml',
+        'hr_employee_data.xml',
+        'hr_employee_termination_workflow.xml',
+        'hr_employee_workflow.xml',
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_employee_state/hr.py'
--- hr_employee_state/hr.py	1970-01-01 00:00:00 +0000
+++ hr_employee_state/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,436 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import time
+
+from datetime import datetime
+
+from openerp import netsvc
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+from openerp.tools.translate import _
+
+
+class hr_employee(osv.Model):
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        # 'state' is already being used by hr_attendance
+        'status': fields.selection([
+            ('new', 'New-Hire'),
+            ('onboarding', 'On-Boarding'),
+            ('active', 'Active'),
+            ('pending_inactive', 'Pending Deactivation'),
+            ('inactive', 'Inactive'),
+            ('reactivated', 'Re-Activated'),
+        ],
+            'Status', readonly=True),
+        'inactive_ids': fields.one2many('hr.employee.termination', 'employee_id', 'Deactivation Records'),
+        'saved_department_id': fields.many2one('hr.department', 'Saved Department'),
+    }
+
+    _defaults = {
+        'status': 'new',
+    }
+
+    def condition_finished_onboarding(self, cr, uid, ids, context=None):
+
+        employee = self.browse(cr, uid, ids[0], context=context)
+        if employee.status == 'onboarding':
+            return True
+
+        return False
+
+    def state_active(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        data = self.read(
+            cr, uid, ids, ['status', 'saved_department_id'], context=context)
+        for d in data:
+            if d['status'] and d['status'] == 'pending_inactive':
+                self.write(cr, uid, d['id'],
+                           {'status': 'active',
+                            'department_id': d['saved_department_id'] and d['saved_department_id'][0] or False,
+                            'saved_department_id': False},
+                           context=context)
+            else:
+                self.write(cr, uid, ids, {'status': 'active'}, context=context)
+
+        return True
+
+    def state_pending_inactive(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        data = self.read(cr, uid, ids, ['department_id'], context=context)
+        for d in data:
+            self.write(cr, uid, d['id'],
+                       {'status': 'pending_inactive',
+                        'saved_department_id': d['department_id'] and d['department_id'][0] or False,
+                        'department_id': False},
+                       context=context)
+
+        return True
+
+    def state_inactive(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        data = self.read(
+            cr, uid, ids, ['status', 'saved_department_id'], context=context)
+        for d in data:
+            vals = {
+                'active': False,
+                'status': 'inactive',
+                'job_id': False,
+            }
+            if d['status'] and d['status'] == 'pending_inactive':
+                vals.update(
+                    {'department_id': d['saved_department_id'] and d['saved_department_id'][0] or False,
+                     'saved_department_id': False})
+
+            self.pool.get('hr.employee').write(
+                cr, uid, d['id'], vals, context=context)
+
+        return True
+
+    def signal_reactivate(self, cr, uid, ids, context=None):
+
+        for employee in self.browse(cr, uid, ids, context=context):
+            self.write(cr, uid, employee.id, {'active': True}, context=context)
+            netsvc.LocalService('workflow').trg_validate(
+                uid, 'hr.employee', employee.id, 'signal_reactivate', cr)
+
+        return True
+
+
+class hr_employee_termination_reason(osv.Model):
+
+    _name = 'hr.employee.termination.reason'
+    _description = 'Reason for Employment Termination'
+
+    _columns = {
+        'name': fields.char('Name', size=256, required=True),
+    }
+
+
+class hr_employee_termination(osv.osv):
+
+    _name = 'hr.employee.termination'
+    _description = 'Data Related to Deactivation of Employee'
+
+    _inherit = ['mail.thread', 'ir.needaction_mixin']
+
+    _columns = {
+        'name': fields.date('Effective Date', required=True, readonly=True,
+                            states={'draft': [('readonly', False)]}),
+        'reason_id': fields.many2one('hr.employee.termination.reason', 'Reason', required=True,
+                                     readonly=True, states={
+                                         'draft': [('readonly', False)]}),
+        'notes': fields.text('Notes', readonly=True, states={'draft': [('readonly', False)]}),
+        'employee_id': fields.many2one('hr.employee', 'Employee', required=True, readonly=True),
+        'department_id': fields.related('employee_id', 'department_id', type='many2one',
+                                        relation='hr.department', store=True, string="Department"),
+        'saved_department_id': fields.related('employee_id', 'saved_department_id', type='many2one',
+                                              relation='hr.department', store=True, string="Department"),
+        'state': fields.selection([
+            ('draft', 'Draft'),
+            ('confirm', 'Confirmed'),
+            ('cancel', 'Cancelled'),
+            ('done', 'Done'),
+        ],
+            'State', readonly=True),
+    }
+
+    _defaults = {
+        'state': 'draft',
+    }
+
+    _track = {
+        'state': {
+            'hr_employee_state.mt_alert_state_confirm': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirm',
+            'hr_employee_state.mt_alert_state_done': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done',
+            'hr_employee_state.mt_alert_state_cancel': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel',
+        },
+    }
+
+    def _needaction_domain_get(self, cr, uid, context=None):
+
+        users_obj = self.pool.get('res.users')
+        domain = []
+
+        if users_obj.has_group(cr, uid, 'base.group_hr_user'):
+            domain = [('state', 'in', ['draft'])]
+
+        if users_obj.has_group(cr, uid, 'base.group_hr_manager'):
+            if len(domain) > 0:
+                domain = ['|'] + domain + [('state', '=', 'confirm')]
+            else:
+                domain = [('state', '=', 'confirm')]
+
+        if len(domain) > 0:
+            return domain
+
+        return False
+
+    def unlink(self, cr, uid, ids, context=None):
+
+        for term in self.browse(cr, uid, ids, context=context):
+            if term.state not in ['draft']:
+                raise osv.except_osv(_('Unable to delete record!'),
+                                     _('Employment termination already in progress. Use the "Cancel" button instead.'))
+
+            # Trigger employee status change back to Active and contract back
+            # to Open
+            wkf = netsvc.LocalService('workflow')
+            wkf.trg_validate(
+                uid, 'hr.employee', term.employee_id.id, 'signal_active', cr)
+            for contract in term.employee_id.contract_ids:
+                if contract.state == 'pending_done':
+                    wkf.trg_validate(
+                        uid, 'hr.contract', contract.id, 'signal_open', cr)
+
+        return super(hr_employee_termination, self).unlink(cr, uid, ids, context=context)
+
+    def effective_date_in_future(self, cr, uid, ids, context=None):
+
+        today = datetime.now().date()
+        for term in self.browse(cr, uid, ids, context=context):
+            effective_date = datetime.strptime(
+                term.name, DEFAULT_SERVER_DATE_FORMAT).date()
+            if effective_date <= today:
+                return False
+
+        return True
+
+    def state_cancel(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        for term in self.browse(cr, uid, ids, context=context):
+
+            # Trigger a status change of the employee and his contract(s)
+            wkf = netsvc.LocalService('workflow')
+            wkf.trg_validate(
+                uid, 'hr.employee', term.employee_id.id, 'signal_active', cr)
+            for contract in term.employee_id.contract_ids:
+                if contract.state == 'pending_done':
+                    wkf.trg_validate(
+                        uid, 'hr.contract', contract.id, 'signal_open', cr)
+
+            self.write(cr, uid, term.id, {'state': 'cancel'}, context=context)
+
+        return True
+
+    def state_done(self, cr, uid, ids, context=None):
+
+        for term in self.browse(cr, uid, ids, context=context):
+            if self.effective_date_in_future(cr, uid, [term.id], context=context):
+                raise osv.except_osv(_('Unable to deactivate employee!'),
+                                     _('Effective date is still in the future.'))
+
+            # Trigger a status change of the employee and any contracts pending
+            # termination.
+            wkf = netsvc.LocalService('workflow')
+            for contract in term.employee_id.contract_ids:
+                if contract.state == 'pending_done':
+                    wkf.trg_validate(
+                        uid, 'hr.contract', contract.id, 'signal_done', cr)
+            wkf.trg_validate(
+                uid, 'hr.employee', term.employee_id.id, 'signal_inactive', cr)
+
+            self.write(cr, uid, term.id, {'state': 'done'}, context=context)
+
+        return True
+
+
+class hr_contract(osv.Model):
+
+    _name = 'hr.contract'
+    _inherit = 'hr.contract'
+
+    def end_contract(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        if len(ids) == 0:
+            return False
+
+        context.update({'end_contract_id': ids[0]})
+        return {
+            'view_type': 'form',
+            'view_mode': 'form',
+            'res_model': 'hr.contract.end',
+            'type': 'ir.actions.act_window',
+            'target': 'new',
+            'context': context
+        }
+
+    def _state_common(self, cr, uid, ids, context=None):
+
+        wkf = netsvc.LocalService('workflow')
+        for contract in self.browse(cr, uid, ids, context=context):
+            if contract.employee_id.status == 'new':
+                wkf.trg_validate(
+                    uid, 'hr.employee', contract.employee_id.id, 'signal_confirm', cr)
+
+    def state_trial(self, cr, uid, ids, context=None):
+        """Override 'trial' contract state to also change employee state: new -> onboarding"""
+
+        res = super(hr_contract, self).state_trial(
+            cr, uid, ids, context=context)
+        self._state_common(cr, uid, ids, context=context)
+        return res
+
+    def state_open(self, cr, uid, ids, context=None):
+        """Override 'open' contract state to also change employee state: new -> onboarding"""
+
+        res = super(hr_contract, self).state_open(
+            cr, uid, ids, context=context)
+        self._state_common(cr, uid, ids, context=context)
+        return res
+
+    def try_signal_contract_completed(self, cr, uid, context=None):
+
+        d = datetime.now().date()
+        ids = self.search(cr, uid, [
+            ('state', '=', 'open'),
+            ('date_end', '<', d.strftime(
+                DEFAULT_SERVER_DATE_FORMAT))
+        ], context=context)
+        if len(ids) == 0:
+            return
+
+        for c in self.browse(cr, uid, ids, context=context):
+            vals = {
+                'name': c.date_end and c.date_end or time.strftime(DEFAULT_SERVER_DATE_FORMAT),
+                'employee_id': c.employee_id.id,
+                'reason': 'contract_end',
+            }
+            self.setup_pending_done(cr, uid, c, vals, context=context)
+
+        return
+
+    def setup_pending_done(self, cr, uid, contract, term_vals, context=None):
+        """Start employee deactivation process."""
+
+        term_obj = self.pool.get('hr.employee.termination')
+        dToday = datetime.now().date()
+
+        # If employee is already inactive simply end the contract
+        wkf = netsvc.LocalService('workflow')
+        if not contract.employee_id.active:
+            wkf.trg_validate(
+                uid, 'hr.contract', contract.id, 'signal_done', cr)
+            return
+
+        # Ensure there are not other open contracts
+        #
+        open_contract = False
+        ee = self.pool.get('hr.employee').browse(
+            cr, uid, contract.employee_id.id, context=context)
+        for c2 in ee.contract_ids:
+            if c2.id == contract.id or c2.state == 'draft':
+                continue
+
+            if (not c2.date_end or datetime.strptime(c2.date_end, DEFAULT_SERVER_DATE_FORMAT).date() >= dToday) and c2.state != 'done':
+                open_contract = True
+
+        # Don't create an employment termination if the employee has an open contract or
+        # if this contract is already in the 'done' state.
+        if open_contract or contract.state == 'done':
+            return
+
+        # Also skip creating an employment termination if there is already one in
+        # progress for this employee.
+        #
+        term_ids = term_obj.search(
+            cr, uid, [('employee_id', '=', contract.employee_id.id),
+                      ('state', 'in', ['draft', 'confirm'])],
+            context=context)
+        if len(term_ids) > 0:
+            return
+
+        term_obj.create(cr, uid, term_vals, context=context)
+
+        # Set the contract state to pending completion
+        wkf = netsvc.LocalService('workflow')
+        wkf.trg_validate(
+            uid, 'hr.contract', contract.id, 'signal_pending_done', cr)
+
+        # Set employee state to pending deactivation
+        wkf.trg_validate(
+            uid, 'hr.employee', contract.employee_id.id, 'signal_pending_inactive', cr)
+
+
+class hr_job(osv.Model):
+
+    _name = 'hr.job'
+    _inherit = 'hr.job'
+
+    # Override calculation of number of employees in job. Remove employees for
+    # which the termination process has already started.
+    #
+    def _no_of_employee(self, cr, uid, ids, name, args, context=None):
+        res = {}
+        count = 0
+        for job in self.browse(cr, uid, ids, context=context):
+            for ee in job.employee_ids:
+                if ee.active and ee.status not in ['pending_inactive']:
+                    count += 1
+
+            res[job.id] = {
+                'no_of_employee': count,
+                'expected_employees': count + job.no_of_recruitment,
+            }
+        return res
+
+    def _get_job_position(self, cr, uid, ids, context=None):
+        res = []
+        data = self.pool.get('hr.employee').read(
+            cr, uid, ids, ['job_id'], context=context)
+        [res.append(d['job_id'][0]) for d in data if d['job_id']]
+        return res
+
+    _columns = {
+        # Override from base class. Also, watch 'status' field of hr.employee
+        'no_of_employee': fields.function(_no_of_employee, string="Current Number of Employees",
+                                          help='Number of employees currently occupying this job position.',
+                                          store={
+                                              'hr.employee': (_get_job_position, ['job_id', 'status'], 10),
+                                          },
+                                          multi='no_of_employee'),
+        'expected_employees': fields.function(_no_of_employee, string='Total Forecasted Employees',
+                                              help='Expected number of employees for this job position after new recruitment.',
+                                              store={
+                                                  'hr.job': (lambda self, cr, uid, ids, c=None: ids, ['no_of_recruitment'], 10),
+                                                  'hr.employee': (_get_job_position, ['job_id', 'status'], 10),
+                                              },
+                                              multi='no_of_employee'),
+    }

=== added file 'hr_employee_state/hr_employee_data.xml'
--- hr_employee_state/hr_employee_data.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_state/hr_employee_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Alert-related subtypes for messaging / Chatter -->
+        
+        <record id="mt_alert_state_confirm" model="mail.message.subtype">
+            <field name="name">Employment Termination - Confirmed</field>
+            <field name="res_model">hr.infraction</field>
+            <field name="description">Employment Termination initiated</field>
+        </record>
+        
+        <record id="mt_alert_state_done" model="mail.message.subtype">
+            <field name="name">Employment Termination - Completed</field>
+            <field name="res_model">hr.infraction</field>
+            <field name="description">Completed</field>
+        </record>
+        
+        <record id="mt_alert_state_cancel" model="mail.message.subtype">
+            <field name="name">Employment Termination - Cancelled</field>
+            <field name="res_model">hr.infraction</field>
+            <field name="description">Cancelled</field>
+        </record>
+        
+        <!-- Employment Termination Reason -->
+        <record id="term_contract_end" model="hr.employee.termination.reason">
+            <field name="name">Contract Ended</field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_employee_state/hr_employee_termination_workflow.xml'
--- hr_employee_state/hr_employee_termination_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_state/hr_employee_termination_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <!-- Workflow Definition -->
+        
+        <record id="wkf_employee_termination" model="workflow">
+            <field name="name">hr.employee.termination.basic</field>
+            <field name="osv">hr.employee.termination</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (States) -->
+        
+        <record id="act_draft" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_termination"/>
+            <field name="name">draft</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'draft'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_confirm" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_termination"/>
+            <field name="name">confirm</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'confirm'})</field>
+        </record>
+        
+        <record id="act_done" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_termination"/>
+            <field name="name">done</field>
+            <field name="kind">function</field>
+            <field name="action">state_done()</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        <record id="act_cancel" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_termination"/>
+            <field name="name">cancel</field>
+            <field name="kind">function</field>
+            <field name="action">state_cancel()</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="draft2confirm" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_confirm"/>
+            <field name="signal">signal_confirmed</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="confirm2cancel" model="workflow.transition">
+            <field name="act_from" ref="act_confirm"/>
+            <field name="act_to" ref="act_cancel"/>
+            <field name="signal">signal_cancel</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="confirm2done" model="workflow.transition">
+            <field name="act_from" ref="act_confirm"/>
+            <field name="act_to" ref="act_done"/>
+            <field name="signal">signal_done</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+
+    </data>
+</openerp>

=== added file 'hr_employee_state/hr_employee_workflow.xml'
--- hr_employee_state/hr_employee_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_state/hr_employee_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <!-- Employee Workflow Definition -->
+        <record id="wkf_employee" model="workflow">
+            <field name="name">hr.employee.basic</field>
+            <field name="osv">hr.employee</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (Stages) -->
+        
+        <record id="act_new" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee"/>
+            <field name="name">new</field>
+            <field name="kind">function</field>
+            <field name="action">write({'status': 'new'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_onboarding" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee"/>
+            <field name="name">onboarding</field>
+            <field name="kind">function</field>
+            <field name="action">write({'status': 'onboarding'})</field>
+        </record>
+        
+        <record id="act_active" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee"/>
+            <field name="name">active</field>
+            <field name="kind">function</field>
+            <field name="action">state_active()</field>
+        </record>
+        
+        <record id="act_pending_inactive" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee"/>
+            <field name="name">pending_inactive</field>
+            <field name="kind">function</field>
+            <field name="action">state_pending_inactive()</field>
+        </record>
+        
+        <record id="act_inactive" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee"/>
+            <field name="name">inactive</field>
+            <field name="kind">function</field>
+            <field name="action">state_inactive()</field>
+        </record>
+        
+        <record id="act_reactivated" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee"/>
+            <field name="name">reactivated</field>
+            <field name="kind">function</field>
+            <field name="action">write({'status': 'reactivated'})</field>
+        </record>
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="new2onboarding" model="workflow.transition">
+            <field name="act_from" ref="act_new"/>
+            <field name="act_to" ref="act_onboarding"/>
+            <field name="signal">signal_confirm</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="onboarding2active" model="workflow.transition">
+            <field name="act_from" ref="act_onboarding"/>
+            <field name="act_to" ref="act_active"/>
+            <field name="condition">condition_finished_onboarding() == True</field>
+            <field name="signal">signal_active</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="new2pendinginactive" model="workflow.transition">
+            <field name="act_from" ref="act_new"/>
+            <field name="act_to" ref="act_pending_inactive"/>
+            <field name="signal">signal_pending_inactive</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="onboarding2pendinginactive" model="workflow.transition">
+            <field name="act_from" ref="act_onboarding"/>
+            <field name="act_to" ref="act_pending_inactive"/>
+            <field name="signal">signal_pending_inactive</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="active2pendinginactive" model="workflow.transition">
+            <field name="act_from" ref="act_active"/>
+            <field name="act_to" ref="act_pending_inactive"/>
+            <field name="signal">signal_pending_inactive</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="pendinginactive2active" model="workflow.transition">
+            <field name="act_from" ref="act_pending_inactive"/>
+            <field name="act_to" ref="act_active"/>
+            <field name="signal">signal_active</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="pendinginactive2inactive" model="workflow.transition">
+            <field name="act_from" ref="act_pending_inactive"/>
+            <field name="act_to" ref="act_inactive"/>
+            <field name="signal">signal_inactive</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="inactive2reactivated" model="workflow.transition">
+            <field name="act_from" ref="act_inactive"/>
+            <field name="act_to" ref="act_reactivated"/>
+            <field name="signal">signal_reactivate</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="reactivated2onboarding" model="workflow.transition">
+            <field name="act_from" ref="act_reactivated"/>
+            <field name="act_to" ref="act_onboarding"/>
+            <field name="signal">signal_confirm_reactivate</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_employee_state/hr_view.xml'
--- hr_employee_state/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_state/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <record id="view_employee_form" model="ir.ui.view">
+            <field name="name">hr.employee.form.inherit.1</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//form/sheet/field[@name='image_medium']" position="before">
+                        <header>
+                            <button name="signal_confirm" type="workflow" string="Confirm"
+                                    attrs="{'invisible': [('status','!=','new')]}" groups="base.group_hr_manager" class="oe_highlight"/>
+                            <button name="signal_active" type="workflow" string="Finished Onboarding"
+                                    attrs="{'invisible': [('status','!=','onboarding')]}" groups="base.group_hr_manager" class="oe_highlight"/>
+                            <!--<button name="%(action_set_inactive)d" type="action" string="Set Inactive"
+                                    attrs="{'invisible': [('status','not in',['new', 'onboarding', 'active'])]}" groups="base.group_hr_manager" class="oe_highlight"/>-->
+                            <button name="signal_reactivate" type="object" string="Re-Activate"
+                                    attrs="{'invisible': [('status','!=','inactive')]}" groups="base.group_hr_manager" class="oe_highlight"/>
+                            <button name="signal_confirm_reactivate" type="workflow" string="Confirm"
+                                    attrs="{'invisible': [('status','!=','reactivated')]}" groups="base.group_hr_manager" class="oe_highlight"/>
+                            <field name="status" widget="statusbar"/>
+                        </header>
+                    </xpath>
+                    <xpath expr="//page[@string='HR Settings']" position="after">
+                        <page string="Deactivation">
+                            <group>
+                                <group string="Deactivation Records" colspan="4" col="4">
+                                    <field name="inactive_ids" nolabel="1">
+                                        <tree string="Inactive Records">
+                                            <field name="name"/>
+                                            <field name="reason_id"/>
+                                            <field name="state"/>
+                                        </tree>
+                                    </field>
+                                </group>
+                            </group>
+                        </page>
+                    </xpath>
+                    <xpath expr="//field[@name='department_id']" position="replace">
+                        <field name="department_id" on_change="onchange_department_id(department_id)" attrs="{'invisible': [('status','=','pending_inactive')]}"/>
+                        <field name="saved_department_id" attrs="{'invisible': [('status','!=','pending_inactive')]}"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+
+        <record id="view_termination_filter" model="ir.ui.view">
+            <field name="name">Employees</field>
+            <field name="model">hr.employee.termination</field>
+            <field name="arch" type="xml">
+                <search string="Employees">
+                    <field name="name" string="Employees"/>
+                    <field name="department_id" />
+                    <filter string="Draft, Confirmed" icon="terp-personal" name="draft_state" domain="[('state','in', ['draft','confirm'])]" help="To be processed"/>
+                    <group expand="0" string="Group By...">
+                        <filter string="Department" icon="terp-personal+" domain="[]" context="{'group_by':'department_id'}"/>
+                    </group>
+                </search>
+             </field>
+         </record>
+         
+        <record id="hr_employee_termination_tree_view" model="ir.ui.view">
+            <field name="name">hr.employee.termination.tree</field>
+            <field name="model">hr.employee.termination</field>
+            <field name="arch" type="xml">
+                <tree string="Employment Terminations">
+                    <field name="employee_id"/>
+                    <field name="department_id"/>
+                    <field name="name"/>
+                    <field name="reason_id"/>
+                    <field name="state"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="hr_employee_termination_form_view" model="ir.ui.view">
+            <field name="name">hr.employee.termination.form</field>
+            <field name="model">hr.employee.termination</field>
+            <field name="arch" type="xml">
+                <form string="Employment Termination" version="7.0">
+                    <sheet>
+                        <header>
+                            <button name="signal_confirmed" type="workflow" states="draft" string="Confirm" class="oe_highlight"/>
+                            <button name="signal_done" type="workflow" states="confirm" string="Deactivate" class="oe_highlight"/>
+                            <button name="signal_cancel" type="workflow" states="confirm" string="Cancel" class="oe_highlight"/>
+                            <field name="state" widget="statusbar"/>
+                        </header>
+                        <group>
+                            <group>
+                                <field name="employee_id"/>
+                                <field name="reason_id"/>
+                                <field name="name"/>
+                            </group>
+                            <group>
+                                <field name="department_id" attrs="{'invisible': [('state','!=','done')]}"/>
+                                <field name="saved_department_id" attrs="{'invisible': [('state','=','done')]}"/>
+                            </group>
+                        </group>
+                        <separator string="Notes"/>
+                        <field name="notes" nolabel="1"/>
+                    </sheet>
+                    <div class="oe_chatter">
+                        <field name="message_follower_ids" widget="mail_followers"/>
+                        <field name="message_ids" widget="mail_thread"/>
+                    </div>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_hr_employee_termination" model="ir.actions.act_window">
+            <field name="name">Employment Termination</field>
+            <field name="res_model">hr.employee.termination</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="context">{'search_default_draft_state': 1}</field>
+            <field name="search_view_id" ref="view_termination_filter"/>
+        </record>
+        <menuitem id="menu_hr_employee_termination"
+            action="open_hr_employee_termination"
+            parent="hr.menu_hr_main"
+            sequence="150"/>
+        
+        <!-- Termination Reason -->
+        
+        <record id="view_termination_reason_tree" model="ir.ui.view">
+            <field name="name">hr.employee.termination.reason.tree</field>
+            <field name="model">hr.employee.termination.reason</field>
+            <field name="arch" type="xml">
+                <tree string="Employment Termination Reasons">
+                    <field name="name"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_termination_reason_form" model="ir.ui.view">
+            <field name="name">hr.employee.termination.reason.form</field>
+            <field name="model">hr.employee.termination.reason</field>
+            <field name="arch" type="xml">
+                <form string="Employment Termination Reason">
+                    <field name="name"/>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_termination_reason" model="ir.actions.act_window">
+            <field name="name">Employment Termination Reasons</field>
+            <field name="res_model">hr.employee.termination.reason</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem id="menu_hr_employee_termination_reason"
+            action="open_termination_reason"
+            parent="hr.menu_hr_configuration"
+            sequence="5"/>
+
+        <!-- HR Contract -->
+        <record id="view_contract_form" model="ir.ui.view">
+            <field name="name">hr.contract.form.inherit.1</field>
+            <field name="model">hr.contract</field>
+            <field name="inherit_id" ref="hr_contract_state.view_contract_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//button[@name='signal_done']" position="replace">
+                        <button name="end_contract" type="object" string="End Contract"
+                                groups="base.group_hr_user" states="trial,trial_ending,open,contract_ending" class="oe_highlight"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_employee_state/security'
=== added file 'hr_employee_state/security/ir.model.access.csv'
--- hr_employee_state/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_employee_state/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,6 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_employee_termination_user,access_hr_employee_termination,model_hr_employee_termination,base.group_hr_user,1,1,1,1
+access_hr_employee_termination_pm,access_hr_employee_termination,model_hr_employee_termination,hr_security.group_payroll_manager,1,0,0,0
+access_hr_employee_termination_reason_user,access_hr_employee_termination_reason,model_hr_employee_termination_reason,base.group_hr_user,1,0,0,0
+access_hr_employee_termination_reason_manager,access_hr_employee_termination_reason,model_hr_employee_termination_reason,base.group_hr_manager,1,1,1,1
+access_hr_employee_termination_reason_pm,access_hr_employee_termination_reason,model_hr_employee_termination_reason,hr_security.group_payroll_manager,1,0,0,0

=== added directory 'hr_employee_state/wizard'
=== added file 'hr_employee_state/wizard/__init__.py'
--- hr_employee_state/wizard/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_employee_state/wizard/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import end_contract

=== added file 'hr_employee_state/wizard/end_contract.py'
--- hr_employee_state/wizard/end_contract.py	1970-01-01 00:00:00 +0000
+++ hr_employee_state/wizard/end_contract.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,88 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from datetime import datetime
+
+import netsvc
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+
+
+class employee_set_inactive(osv.TransientModel):
+
+    _name = 'hr.contract.end'
+    _description = 'Employee De-Activation Wizard'
+
+    _columns = {
+        'contract_id': fields.many2one('hr.contract', 'Contract', readonly=True),
+        'employee_id': fields.many2one('hr.employee', 'Employee', required=True, readonly=True),
+        'date': fields.date('Date', required=True),
+        'reason_id': fields.many2one('hr.employee.termination.reason', 'Reason', required=True),
+        'notes': fields.text('Notes'),
+    }
+
+    def _get_contract(self, cr, uid, context=None):
+
+        if context == None:
+            context = {}
+
+        return context.get('end_contract_id', False)
+
+    def _get_employee(self, cr, uid, context=None):
+
+        if context == None:
+            context = {}
+
+        contract_id = context.get('end_contract_id', False)
+        if not contract_id:
+            return False
+
+        data = self.pool.get(
+            'hr.contract').read(cr, uid, contract_id, ['employee_id'],
+                                context=context)
+        return data['employee_id'][0]
+
+    _defaults = {
+        'date': datetime.now().strftime(DEFAULT_SERVER_DATE_FORMAT),
+        'employee_id': _get_employee,
+        'contract_id': _get_contract,
+    }
+
+    def set_employee_inactive(self, cr, uid, ids, context=None):
+
+        data = self.read(
+            cr, uid, ids[0], [
+                'employee_id', 'contract_id', 'date', 'reason_id', 'notes'],
+            context=context)
+        vals = {
+            'name': data['date'],
+            'employee_id': data['employee_id'][0],
+            'reason_id': data['reason_id'][0],
+            'notes': data['notes'],
+        }
+
+        contract_obj = self.pool.get('hr.contract')
+        contract = contract_obj.browse(
+            cr, uid, data['contract_id'][0], context=context)
+        contract_obj.setup_pending_done(
+            cr, uid, contract, vals, context=context)
+
+        return {'type': 'ir.actions.act_window_close'}

=== added file 'hr_employee_state/wizard/end_contract_view.xml'
--- hr_employee_state/wizard/end_contract_view.xml	1970-01-01 00:00:00 +0000
+++ hr_employee_state/wizard/end_contract_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="action_set_inactive" model="ir.actions.act_window">
+            <field name="name">Employee De-Activation Wizard</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">hr.contract.end</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form</field>
+            <field name="target">new</field>
+        </record>
+        
+        <record id="view_inactive_wizard" model="ir.ui.view">
+            <field name="name">hr.contract.end.form</field>
+            <field name="model">hr.contract.end</field>
+            <field name="arch" type="xml">
+                <form string="Employee De-Activation Wizard" version="7.0">
+                    <header>
+                        <button name="set_employee_inactive" type="object" string="End Employment" class="oe_highlight"/>
+                    </header>
+                    <newline/>
+                    <div>
+                        <b>
+                        This employee no longer has any valid contracts. In such a case it is
+                        customary for the employee record to be archived as an 'inactive' record.
+                        However, the record and all the employee's associated data such as contracts,
+                        leaves, attendances, etc will still be available. This is not a
+                        permanent removal of the employee record. Should you wish to you can
+                        easily re-activate it again at any time in the future.
+                        </b>
+                    </div>
+                    <group>
+                        <group>
+                            <field name="contract_id" invisible="1"/>
+                            <field name="employee_id"/>
+                            <field name="date"/>
+                            <field name="reason_id"/>
+                        </group>
+                        <group></group>
+                    </group>
+                    <group string="Notes" colspan="4">
+                        <field name="notes" nolabel="1"/>
+                    </group>
+                </form>
+            </field>
+        </record>
+    
+    </data>
+</openerp>

=== added directory 'hr_family'
=== added file 'hr_family/__init__.py'
--- hr_family/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_family/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr

=== added file 'hr_family/__openerp__.py'
--- hr_family/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_family/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,49 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Family Information',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Employee Family Information
+===========================
+
+    Enter extra information about employee's family
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://www.openerp.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_family/hr.py'
--- hr_family/hr.py	1970-01-01 00:00:00 +0000
+++ hr_family/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,51 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from osv import fields, osv
+
+
+class hr_children(osv.osv):
+
+    _name = 'hr.employee.children'
+    _description = 'HR Employee Children'
+
+    _columns = {
+        'name': fields.char('Name', size=256, required=True),
+        'dob': fields.date('Date of Birth'),
+        'employee_id': fields.many2one('hr.employee', 'Employee'),
+    }
+
+
+class hr_employee(osv.osv):
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        'fam_spouse': fields.char("Name", size=256),
+        'fam_spouse_employer': fields.char("Employer", size=256),
+        'fam_spouse_tel': fields.char("Telephone.", size=32),
+        'fam_children_ids': fields.one2many('hr.employee.children', 'employee_id', 'Children'),
+        'fam_father': fields.char("Father's Name", size=128),
+        'fam_father_dob': fields.date('Date of Birth'),
+        'fam_mother': fields.char("Mother's Name", size=128),
+        'fam_mother_dob': fields.date('Date of Birth'),
+    }

=== added file 'hr_family/hr_view.xml'
--- hr_family/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_family/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+##############################################################################
+#
+#    Copyright (C) 2011 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+-->
+
+<openerp>
+    <data>
+        
+        <record id="hr_employee_view_form" model="ir.ui.view">
+            <field name="name">hr.employee.view.form.inherit.familyinfo</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <xpath expr='//page[@string="Personal Information"]' position="after">
+                    <page string="Family">
+                        <group>
+                            <group string="Spouse">
+                                <field name="fam_spouse"/>
+                                <field name="fam_spouse_employer"/>
+                                <field name="fam_spouse_tel"/>                            
+                            </group>
+                            <group string="Parents">
+                                <field name="fam_father"/>
+                                <field name="fam_father_dob"/>
+                                <field name="fam_mother"/>
+                                <field name="fam_mother_dob"/>
+                            </group>
+                        </group>
+                        <group string="Children">
+                            <field name="fam_children_ids" nolabel="1"/>
+                        </group>
+                    </page>
+                </xpath>
+            </field>
+        </record>
+        
+        <record id="hr_children_view_tree" model="ir.ui.view">
+            <field name="name">hr.children.view.tree</field>
+            <field name="model">hr.employee.children</field>
+            <field name="type">tree</field>
+            <field name="arch" type="xml">
+                <tree string="Employee Children">
+                    <field name="name"/>
+                    <field name="dob"/>
+                </tree>
+            </field>
+        </record>
+        <record id="hr_children_view_form" model="ir.ui.view">
+            <field name="name">hr.children.view.form</field>
+            <field name="model">hr.employee.children</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="Employee Children">
+                    <field name="name"/>
+                    <field name="dob"/>
+                </form>
+            </field>
+        </record>
+        
+    </data>
+</openerp>
\ No newline at end of file

=== added directory 'hr_family/security'
=== added file 'hr_family/security/ir.model.access.csv'
--- hr_family/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_family/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,2 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+"access_hr_employee_children_user","hr.employee.children_user","model_hr_employee_children","base.group_hr_user",1,1,1,1

=== added directory 'hr_holidays_extension'
=== added file 'hr_holidays_extension/__init__.py'
--- hr_holidays_extension/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_holidays

=== added file 'hr_holidays_extension/__openerp__.py'
--- hr_holidays_extension/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,64 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'HR Holidays Extension',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Extended Capabilities for HR Holidays (Leaves)
+==============================================
+
+    * When calculating the number of leave days take into account the employee's schedule and
+      public holidays
+    * The 'Need Action' mechanism assumes the HR Manager approves leave requests
+    * Rename 'Leave Requests' menu item to 'My Leaves' (which is closer to its intent)
+    * Add a new menu item: All Leave Requests
+    * New way of entering leaves based on the number of days requested, rather
+      than by specifying a start and end date. You tell it how many days to
+      grant and it calculates the start and end dates based on the employee's schedule.
+    * Allow a manager to approve the leave requests of subordinates (manager must be
+      immediate superior of employee or manager of employee's department and have
+      leave approval rights)
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr_holidays',
+        'hr_public_holidays',
+        'hr_schedule',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/user_groups.xml',
+        'security/ir.model.access.csv',
+        'security/ir_rule.xml',
+        'hr_holidays_workflow.xml',
+        'hr_holidays_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_holidays_extension/hr_holidays.py'
--- hr_holidays_extension/hr_holidays.py	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/hr_holidays.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,313 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
+#    and 2004-2010 Tiny SPRL (<http://tiny.be>).
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from datetime import datetime, timedelta
+from pytz import timezone, utc
+
+from openerp import tools
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as OE_DTFORMAT
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DFORMAT
+from openerp.tools.translate import _
+
+
+class hr_holidays_status(osv.Model):
+
+    _inherit = 'hr.holidays.status'
+
+    _columns = {
+        'ex_rest_days': fields.boolean('Exclude Rest Days',
+                                       help="If enabled, the employee's day off is skipped in leave days calculation."),
+        'ex_public_holidays': fields.boolean('Exclude Public Holidays',
+                                             help="If enabled, public holidays are skipped in leave days calculation."),
+    }
+
+
+class hr_holidays(osv.osv):
+
+    _name = 'hr.holidays'
+    _inherit = ['hr.holidays', 'ir.needaction_mixin']
+
+    _columns = {
+        'real_days': fields.float('Total Days', digits=(16, 1)),
+        'rest_days': fields.float('Rest Days', digits=(16, 1)),
+        'public_holiday_days': fields.float('Public Holidays', digits=(16, 1)),
+        'return_date': fields.char('Return Date', size=32),
+    }
+
+    def _employee_get(self, cr, uid, context=None):
+
+        if context == None:
+            context = {}
+
+        # If the user didn't enter from "My Leaves" don't pre-populate Employee
+        # field
+        import logging
+        _l = logging.getLogger(__name__)
+        _l.warning('context: %s', context)
+        if not context.get('search_default_my_leaves', False):
+            return False
+
+        ids = self.pool.get('hr.employee').search(
+            cr, uid, [('user_id', '=', uid)], context=context)
+        if ids:
+            return ids[0]
+        return False
+
+    def _days_get(self, cr, uid, context=None):
+
+        if context == None:
+            context = {}
+
+        date_from = context.get('default_date_from')
+        date_to = context.get('default_date_to')
+        if date_from and date_to:
+            delta = datetime.strptime(
+                date_to, OE_DTFORMAT) - datetime.strptime(date_from, OE_DTFORMAT)
+            return (delta.days and delta.days or 1)
+        return False
+
+    _defaults = {
+        'employee_id': _employee_get,
+        'number_of_days_temp': _days_get,
+    }
+
+    _order = 'date_from asc, type desc'
+
+    def _needaction_domain_get(self, cr, uid, context=None):
+
+        users_obj = self.pool.get('res.users')
+        domain = []
+
+        if users_obj.has_group(cr, uid, 'base.group_hr_manager'):
+            domain = [('state', 'in', ['draft', 'confirm'])]
+            return domain
+
+        elif users_obj.has_group(cr, uid, 'hr_holidays_extension.group_hr_leave'):
+            domain = [('state', 'in', ['confirm']), (
+                'employee_id.user_id', '!=', uid)]
+            return domain
+
+        return False
+
+    def onchange_bynumber(self, cr, uid, ids, no_days, date_from, employee_id, holiday_status_id, context=None):
+        """
+        Update the dates based on the number of days requested.
+        """
+
+        ee_obj = self.pool.get('hr.employee')
+        holiday_obj = self.pool.get('hr.holidays.public')
+        sched_tpl_obj = self.pool.get('hr.schedule.template')
+        sched_detail_obj = self.pool.get('hr.schedule.detail')
+        result = {'value': {}}
+
+        if not no_days or not date_from or not employee_id:
+            return result
+
+        user = self.pool.get('res.users').browse(cr, uid, uid)
+        if user and user.tz:
+            local_tz = timezone(user.tz)
+        else:
+            local_tz = timezone('Africa/Addis_Ababa')
+
+        dt = datetime.strptime(date_from, OE_DTFORMAT)
+        employee = ee_obj.browse(cr, uid, employee_id, context=context)
+        if holiday_status_id:
+            hs_data = self.pool.get(
+                'hr.holidays.status').read(cr, uid, holiday_status_id,
+                                           ['ex_rest_days', 'ex_public_holidays'],
+                                           context=context)
+        else:
+            hs_data = {}
+        ex_rd = hs_data.get('ex_rest_days', False)
+        ex_ph = hs_data.get('ex_public_holidays', False)
+
+        # Get rest day and the schedule start time on the date the leave begins
+        #
+        rest_days = []
+        times = tuple()
+        if ex_rd:
+            if employee.contract_id and employee.contract_id.schedule_template_id:
+                rest_days = sched_tpl_obj.get_rest_days(cr, uid,
+                                                        employee.contract_id.schedule_template_id.id,
+                                                        context=context)
+                times = sched_detail_obj.scheduled_begin_end_times(
+                    cr, uid, employee.id,
+                    employee.contract_id.id, dt,
+                    context=context)
+        if len(times) > 0:
+            utcdtStart = times[0][0]
+        else:
+            dtStart = local_tz.localize(
+                datetime.strptime(dt.strftime(OE_DFORMAT) + ' 00:00:00',  OE_DTFORMAT), is_dst=False)
+            utcdtStart = dtStart.astimezone(utc)
+
+        count_days = no_days
+        real_days = 1
+        ph_days = 0
+        r_days = 0
+        next_dt = dt
+        while count_days > 1:
+            public_holiday = holiday_obj.is_public_holiday(
+                cr, uid, next_dt.date(), context=context)
+            public_holiday = (public_holiday and ex_ph)
+            rest_day = (next_dt.weekday() in rest_days and ex_rd)
+            next_dt += timedelta(days=+1)
+            if public_holiday or rest_day:
+                if public_holiday:
+                    ph_days += 1
+                elif rest_day:
+                    r_days += 1
+                real_days += 1
+                continue
+            else:
+                count_days -= 1
+                real_days += 1
+        while (next_dt.weekday() in rest_days and ex_rd) or (holiday_obj.is_public_holiday(cr, uid, next_dt.date(), context=context) and ex_ph):
+            if holiday_obj.is_public_holiday(cr, uid, next_dt.date(), context=context):
+                ph_days += 1
+            elif next_dt.weekday() in rest_days:
+                r_days += 1
+            next_dt += timedelta(days=1)
+            real_days += 1
+
+        # Set end time based on schedule
+        #
+        times = sched_detail_obj.scheduled_begin_end_times(
+            cr, uid, employee.id,
+            employee.contract_id.id, next_dt,
+            context=context)
+        if len(times) > 0:
+            utcdtEnd = times[-1][1]
+        else:
+            dtEnd = local_tz.localize(
+                datetime.strptime(next_dt.strftime(OE_DFORMAT) + ' 23:59:59',  OE_DTFORMAT), is_dst=False)
+            utcdtEnd = dtEnd.astimezone(utc)
+
+        result['value'].update({'department_id': employee.department_id.id,
+                                'date_from': utcdtStart.strftime(OE_DTFORMAT),
+                                'date_to': utcdtEnd.strftime(OE_DTFORMAT),
+                                'rest_days': r_days,
+                                'public_holiday_days': ph_days,
+                                'real_days': real_days})
+        return result
+
+    def onchange_enddate(self, cr, uid, ids, employee_id, date_to, holiday_status_id, context=None):
+
+        ee_obj = self.pool.get('hr.employee')
+        holiday_obj = self.pool.get('hr.holidays.public')
+        sched_tpl_obj = self.pool.get('hr.schedule.template')
+        res = {'value': {'return_date': False}}
+
+        if not employee_id or not date_to:
+            return res
+
+        if holiday_status_id:
+            hs_data = self.pool.get(
+                'hr.holidays.status').read(cr, uid, holiday_status_id,
+                                           ['ex_rest_days', 'ex_public_holidays'],
+                                           context=context)
+        else:
+            hs_data = {}
+        ex_rd = hs_data.get('ex_rest_days', False)
+        ex_ph = hs_data.get('ex_public_holidays', False)
+
+        rest_days = []
+        if ex_rd:
+            ee = ee_obj.browse(cr, uid, employee_id, context=context)
+            if ee.contract_id and ee.contract_id.schedule_template_id:
+                rest_days = sched_tpl_obj.get_rest_days(cr, uid,
+                                                        ee.contract_id.schedule_template_id.id,
+                                                        context=context)
+
+        dt = datetime.strptime(date_to, OE_DTFORMAT)
+        return_date = dt + timedelta(days=+1)
+        while (return_date.weekday() in rest_days and ex_rd) or (holiday_obj.is_public_holiday(cr, uid, return_date.date(), context=context) and ex_ph):
+            return_date += timedelta(days=1)
+        res['value']['return_date'] = return_date.strftime('%B %d, %Y')
+        return res
+
+    def create(self, cr, uid, vals, context=None):
+
+        att_obj = self.pool.get('hr.attendance')
+        if vals.get('date_from', False) and vals.get('date_to', False) and (not vals.get('type', False) or vals.get('type', 'x') == 'remove') and vals.get('holiday_type', 'x') == 'employee':
+            att_ids = att_obj.search(
+                cr, uid, [('employee_id', '=', vals['employee_id']),
+                          ('name', '>=', vals[
+                              'date_from']),
+                          ('name', '<=', vals['date_to'])],
+                context=context)
+            if len(att_ids) > 0:
+                raise osv.except_osv(_('Warning'),
+                                     _('There is already one or more attendance records for the date you have chosen.'))
+
+        return super(hr_holidays, self).create(cr, uid, vals, context=context)
+
+    def holidays_first_validate(self, cr, uid, ids, context=None):
+
+        self._check_validate(cr, uid, ids, context=context)
+        return super(hr_holidays, self).holidays_first_validate(cr, uid, ids, context=context)
+
+    def holidays_validate(self, cr, uid, ids, context=None):
+
+        self._check_validate(cr, uid, ids, context=context)
+        return super(hr_holidays, self).holidays_validate(cr, uid, ids, context=context)
+
+    def _check_validate(self, cr, uid, ids, context=None):
+
+        users_obj = self.pool.get('res.users')
+
+        if not users_obj.has_group(cr, uid, 'base.group_hr_manager'):
+            for leave in self.browse(cr, uid, ids, context=context):
+                if leave.employee_id.user_id.id == uid:
+                    raise osv.except_osv(
+                        _('Warning!'), _('You cannot approve your own leave:\nHoliday Type: %s\nEmployee: %s') %
+                        (leave.holiday_status_id.name, leave.employee_id.name))
+        return
+
+
+class hr_attendance(osv.Model):
+
+    _name = 'hr.attendance'
+    _inherit = 'hr.attendance'
+
+    def create(self, cr, uid, vals, context=None):
+
+        if vals.get('name', False):
+            lv_ids = self.pool.get(
+                'hr.holidays').search(cr, uid, [('employee_id', '=', vals['employee_id']),
+                                                ('type', '=', 'remove'),
+                                                ('date_from', '<=', vals[
+                                                    'name']),
+                                                ('date_to', '>=', vals[
+                                                    'name']),
+                                                ('state', 'not in', ['cancel', 'refuse'])],
+                                      context=context)
+            if len(lv_ids) > 0:
+                ee_data = self.pool.get(
+                    'hr.employee').read(cr, uid, vals['employee_id'], ['name'],
+                                        context=context)
+                raise osv.except_osv(_('Warning'),
+                                     _('There is already one or more leaves recorded for the date you have chosen:\nEmployee: %s\nDate: %s' % (ee_data['name'], vals['name'])))
+
+        return super(hr_attendance, self).create(cr, uid, vals, context=context)

=== added file 'hr_holidays_extension/hr_holidays_view.xml'
--- hr_holidays_extension/hr_holidays_view.xml	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/hr_holidays_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+
+        <!-- Make Leave Requests to Approve viewable to managers with Leave Approval rights -->
+        <delete model="ir.ui.menu" id="hr_holidays.menu_request_approve_holidays"/>
+        <menuitem name="Leave Requests to Approve" parent="hr_holidays.menu_open_ask_holidays" id="hr_holidays.menu_request_approve_holidays" action="hr_holidays.request_approve_holidays" groups="base.group_hr_user,hr_holidays_extension.group_hr_leave"/>
+
+        <record id="view_leave_request_form" model="ir.ui.view">
+            <field name="name">Leave Request</field>
+            <field name="model">hr.holidays</field>
+            <field name="priority">1</field>
+            <field name="inherit_id" ref="hr_holidays.edit_holiday_new"/>
+            <field name="arch" type="xml">
+                <xpath expr="//form" position="replace">
+                <form string="Leave Request" version="7.0">
+                <header>
+                    <button string="Approve" name="validate" states="confirm" type="workflow" groups="hr_holidays_extension.group_hr_leave" class="oe_highlight"/>
+                    <button string="Validate" name="second_validate" states="validate1" type="workflow" groups="base.group_hr_user" class="oe_highlight"/>
+                    <button string="Refuse" name="refuse" states="confirm,validate" type="workflow" groups="hr_holidays_extension.group_hr_leave"/>
+                    <button string="Refuse" name="refuse" states="validate1" type="workflow" groups="base.group_hr_user"/>
+                    <button string="Reset to New" name="set_to_draft" states="refuse" type="object" groups="hr_holidays_extension.group_hr_leave"/>
+                    <field name="state" widget="statusbar" statusbar_visible="draft,confirm,validate" statusbar_colors='{"confirm":"blue","validate1":"blue","refuse":"red"}'/>
+                </header>
+                <sheet string="Leave Request">
+                    <group>
+                        <group>
+                            <field name="holiday_type" invisible="1"/>
+                            <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','=','category')]}" on_change="onchange_bynumber(number_of_days_temp, date_from, employee_id, holiday_status_id)" groups="base.group_hr_user"/>
+                            <field name="holiday_status_id" context="{'employee_id':employee_id}"/>
+                            <field name="number_of_days_temp" on_change="onchange_bynumber(number_of_days_temp, date_from, employee_id, holiday_status_id)" string="Days Requested" required="1" readonly="0"/>
+                            <label for="date_from" string="Duration" help="The default duration interval between the start date and the end date is 8 hours.  Feel free to adapt it to your needs."/>
+                            <div>
+                                <group col="3">
+                                    <field name="date_from" nolabel="1" on_change="onchange_bynumber(number_of_days_temp, date_from, employee_id, holiday_status_id)" required="1" class="oe_inline"/><label string="-" class="oe_inline"/>
+                                    <field name="date_to" nolabel="1" on_change="onchange_enddate(employee_id, date_to, holiday_status_id)" required="1" class="oe_inline"/>
+                                </group>
+                            </div>
+                        </group>
+                        <group>
+                            <field name="name" attrs="{'readonly':[('state','!=','draft'),('state','!=','confirm')]}"/>
+                            <field name="department_id" readonly="1" groups="base.group_hr_user"/>
+                        </group>
+                    </group>
+                    <group>
+                        <group>
+                            <field name="return_date"/>
+                        </group>
+                        <group>
+                            <field name="rest_days"/>
+                            <field name="public_holiday_days"/>
+                            <field name="real_days"/>
+                            <field name="holiday_type" invisible="1"/>
+                        </group>
+                    </group>
+                </sheet>
+                <div class="oe_chatter">
+                    <field name="message_follower_ids" widget="mail_followers"/>
+                    <field name="message_ids" widget="mail_thread"/>
+                </div>
+                </form>
+                </xpath>
+            </field>
+        </record>
+
+        <record model="ir.actions.act_window" id="open_leave_request">
+            <field name="name">All Leave Requests</field>
+            <field name="res_model">hr.holidays</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form,calendar</field>
+            <field name="context">{'default_type': 'remove', 'search_default_group_date_from':1, 'search_default_group_type':1}</field>
+            <field name="domain">[('type','=','remove')]</field>
+            <field name="search_view_id" ref="hr_holidays.view_hr_holidays_filter"/>
+            <field name="help" type="html">
+              <p class="oe_view_nocontent_create">
+                Click to create a new leave request.
+              </p><p>
+                Once you have recorded your leave request, it will be sent
+                to a manager for validation. Be sure to set the right leave
+                type (recuperation, legal holidays, sickness) and the exact
+                number of open days related to your leave.
+              </p>
+            </field>
+        </record>
+        <menuitem parent="hr_holidays.menu_open_ask_holidays"
+            id="menu_leave_request"
+            action="open_leave_request"
+            groups="base.group_hr_user"
+            sequence="1"/>
+
+        <record model="ir.actions.act_window.view" id="action_open_leave_request_tree">
+            <field name="sequence" eval="2"/>
+            <field name="view_mode">tree</field>
+            <field name="view_id" ref="hr_holidays.view_holiday"/>
+            <field name="act_window_id" ref="open_leave_request"/>
+        </record>
+        
+        <!-- Change name of leave request action (from a stock install) -->
+        <record model="ir.actions.act_window" id="hr_holidays.open_ask_holidays">
+            <field name="name">My Leaves</field>
+        </record>
+        <menuitem id="hr_holidays.menu_open_ask_holidays_new"
+            parent="hr_holidays.menu_open_ask_holidays"
+            action="hr_holidays.open_ask_holidays"
+            string="My Leaves"/>
+
+        <record id="view_holiday_status_form" model="ir.ui.view">
+            <field name="name">hr.holidays.status.form.extension</field>
+            <field name="model">hr.holidays.status</field>
+            <field name="inherit_id" ref="hr_holidays.edit_holiday_status_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='active']" position="after">
+                        <field name="ex_rest_days"/>
+                        <field name="ex_public_holidays"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_holidays_extension/hr_holidays_workflow.xml'
--- hr_holidays_extension/hr_holidays_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/hr_holidays_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,119 @@
+<?xml version="1.0" ?>
+<openerp>
+<data>
+
+    <!-- Workflow definition 
+        1. draft->submitted (no signal)
+        2. submitted->accepted (validate signal) if not double_validation
+        2. submitted -> first_accepted (validate signal) if double_validation
+        2. submitted->refused (refuse signal)
+        3. accepted->refused (refuse signal)
+        4. first_accepted -> accepted (second_validate  signal)
+        4. first_accepted -> refused (refuse signal)
+
+    -->
+
+    <record model="workflow" id="wkf_holidays">
+        <field name="name">hr.wkf.holidays.extension</field>
+        <field name="osv">hr.holidays</field>
+        <field name="on_create">True</field>
+    </record>
+
+    <record model="workflow.activity" id="act_draft"> <!-- draft -->
+        <field name="wkf_id" ref="wkf_holidays" />
+        <field name="flow_start">True</field>
+        <field name="name">draft</field>
+    </record>
+
+    <record model="workflow.activity" id="act_confirm"> <!-- submitted -->
+        <field name="wkf_id" ref="wkf_holidays" />
+        <field name="name">confirm</field>
+        <field name="kind">function</field>
+        <field name="action">holidays_confirm()</field>
+        <field name="split_mode">OR</field>
+    </record>
+
+    <record model="workflow.activity" id="act_validate"> <!-- accepted -->
+        <field name="wkf_id" ref="wkf_holidays" />
+        <field name="name">validate</field>
+        <field name="kind">function</field>
+        <field name="action">holidays_validate()</field>
+    </record>
+
+    <record model="workflow.activity" id="act_validate1"> <!-- first_accepted -->
+        <field name="wkf_id" ref="wkf_holidays" />
+        <field name="name">first_validate</field>
+        <field name="kind">function</field>
+        <field name="action">holidays_first_validate()</field>
+        <field name="split_mode">OR</field>
+    </record>
+
+
+    <record model="workflow.activity" id="act_refuse"> <!-- refused -->
+        <field name="wkf_id" ref="wkf_holidays" />
+        <field name="name">refuse</field>
+        <field name="flow_stop">True</field>
+        <field name="kind">function</field>
+        <field name="action">holidays_refuse()</field>
+    </record>
+
+    <!--
+        workflow transition
+    -->
+
+    <record model="workflow.transition" id="holiday_draft2confirm"> <!-- 1. draft->submitted (no signal) -->
+        <field name="act_from" ref="act_draft" />
+        <field name="act_to" ref="act_confirm" />
+    </record>
+
+    <record model="workflow.transition" id="holiday_confirm2validate"> <!-- 2. submitted->accepted (validate signal) if not double_validation-->
+        <field name="act_from" ref="act_confirm" />
+        <field name="act_to" ref="act_validate" />
+        <field name="signal">validate</field>
+        <field name="condition">not double_validation</field>
+        <field name="group_id" ref="hr_holidays_extension.group_hr_leave"/>
+    </record>
+
+    <record model="workflow.transition" id="holiday_confirm2validate1"> <!-- 2. submitted -> first_accepted (validate signal) if double_validation-->
+        <field name="act_from" ref="act_confirm" />
+        <field name="act_to" ref="act_validate1" />
+        <field name="signal">validate</field>
+        <field name="condition">double_validation</field>
+        <field name="group_id" ref="hr_holidays_extension.group_hr_leave"/>
+    </record>
+
+
+    <record model="workflow.transition" id="holiday_confirm2refuse"> <!-- 2. submitted->refused (refuse signal) -->
+        <field name="act_from" ref="act_confirm" />
+        <field name="act_to" ref="act_refuse" />
+        <field name="signal">refuse</field>
+        <field name="condition">True</field>
+        <field name="group_id" ref="hr_holidays_extension.group_hr_leave"/>
+    </record>
+
+    <record model="workflow.transition" id="holiday_validate2refuse"> <!-- 3. accepted->refused (refuse signal) -->
+        <field name="act_from" ref="act_validate" />
+        <field name="act_to" ref="act_refuse" />
+        <field name="signal">refuse</field>
+        <field name="condition">True</field>
+        <field name="group_id" ref="hr_holidays_extension.group_hr_leave"/>
+    </record>
+
+    <record model="workflow.transition" id="holiday_validate1_validate"> <!-- 4. first_accepted -> accepted (second_validate  signal) -->
+        <field name="act_from" ref="act_validate1" />
+        <field name="act_to" ref="act_validate" />
+        <field name="condition">True</field>
+        <field name="signal">second_validate</field>
+        <field name="group_id" ref="base.group_hr_user"/>
+    </record>
+
+    <record model="workflow.transition" id="holiday_validate1_refuse"> <!-- 4. first_accepted->refused (refuse signal) -->
+        <field name="act_from" ref="act_validate1" />
+        <field name="act_to" ref="act_refuse" />
+        <field name="signal">refuse</field>
+        <field name="condition">True</field>
+        <field name="group_id" ref="base.group_hr_user"/>
+    </record>
+
+</data>
+</openerp>

=== added directory 'hr_holidays_extension/security'
=== added file 'hr_holidays_extension/security/ir.model.access.csv'
--- hr_holidays_extension/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_holidays_approval,hr.holidays leave approval,model_hr_holidays,hr_holidays_extension.group_hr_leave,1,1,0,0
+access_hr_holydays_status_approval,hr.holidays.status leave approval,model_hr_holidays_status,hr_holidays_extension.group_hr_leave,1,1,0,0
+access_resource_calendar_leaves_approval_user,resource_calendar_leaves_approval_user,resource.model_resource_calendar_leaves,hr_holidays_extension.group_hr_leave,1,1,1,1
+access_crm_meeting_type_approval_user,crm.meeting.type.approval.user,base_calendar.model_crm_meeting_type,hr_holidays_extension.group_hr_leave,1,1,1,1

=== added file 'hr_holidays_extension/security/ir_rule.xml'
--- hr_holidays_extension/security/ir_rule.xml	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/security/ir_rule.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<openerp>
+  <data>
+      
+    <record id="property_rule_holidays_supervisor" model="ir.rule">
+        <field name="name">Supervisor Holidays</field>
+        <field model="ir.model" name="model_id" ref="model_hr_holidays"/>
+        <field name="domain_force">['|', ('employee_id', 'child_of', [user.employee_ids[0].id]), ('employee_id.department_id.manager_id.user_id.id', '=', user.id)]</field>
+        <field name="groups" eval="[(4,ref('hr_holidays_extension.group_hr_leave'))]"/>
+    </record>
+    
+  </data>
+</openerp>

=== added file 'hr_holidays_extension/security/user_groups.xml'
--- hr_holidays_extension/security/user_groups.xml	1970-01-01 00:00:00 +0000
+++ hr_holidays_extension/security/user_groups.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data noupdate="0">
+        
+        <!-- Leave Approval User Group -->
+        
+        <record id="group_hr_leave" model="res.groups">
+            <field name="name">Leave Approval</field>
+            <field name="category_id" ref="base.module_category_human_resources"/>
+            <field name="users" eval="[(4, ref('base.user_root'))]"/>
+            <field name="comment">the user can approve leaves</field>
+        </record>
+        
+    </data>    
+</openerp>

=== added directory 'hr_infraction'
=== added file 'hr_infraction/__init__.py'
--- hr_infraction/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_infraction/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,23 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_infraction
+from . import wizard

=== added file 'hr_infraction/__openerp__.py'
--- hr_infraction/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_infraction/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,53 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Employee Infraction Management',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Warning/Disciplinary Action Management
+========================================
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+        'hr_employee_state',
+        'hr_security',
+        'hr_transfer',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'wizard/action.xml',
+        'hr_infraction_data.xml',
+        'hr_infraction_view.xml',
+        'hr_infraction_workflow.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_infraction/hr_infraction.py'
--- hr_infraction/hr_infraction.py	1970-01-01 00:00:00 +0000
+++ hr_infraction/hr_infraction.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,194 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import time
+
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+from openerp.tools.translate import _
+
+
+class hr_infraction_category(osv.Model):
+
+    _name = 'hr.infraction.category'
+    _description = 'Infraction Type'
+
+    _columns = {
+        'name': fields.char('Name', required=True),
+        'code': fields.char('Code', required=True),
+    }
+
+
+class hr_infraction(osv.Model):
+
+    _name = 'hr.infraction'
+    _description = 'Infraction'
+
+    _inherit = ['mail.thread', 'ir.needaction_mixin']
+
+    _columns = {
+        'name': fields.char('Subject', size=256, required=True, readonly=True,
+                            states={'draft': [('readonly', False)]}),
+        'date': fields.date('Date', required=True, readonly=True,
+                            states={'draft': [('readonly', False)]}),
+        'employee_id': fields.many2one('hr.employee', 'Employee', required=True, readonly=True,
+                                       states={
+                                           'draft': [('readonly', False)]}),
+        'category_id': fields.many2one('hr.infraction.category', 'Category', required=True,
+                                       readonly=True, states={
+                                           'draft': [('readonly', False)]}),
+        'action_ids': fields.one2many('hr.infraction.action', 'infraction_id', 'Actions',
+                                      readonly=True),
+        'memo': fields.text('Description', readonly=True, states={'draft': [('readonly', False)]}),
+        'state': fields.selection([('draft', 'Draft'),
+                                   ('confirm', 'Confirmed'),
+                                   ('action', 'Actioned'),
+                                   ('noaction', 'No Action'),
+                                   ],
+                                  'State', readonly=True),
+    }
+
+    _defaults = {
+        'date': time.strftime(DEFAULT_SERVER_DATE_FORMAT),
+        'state': 'draft',
+    }
+
+    _track = {
+        'state': {
+            'hr_infraction.mt_alert_infraction_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirm',
+            'hr_infraction.mt_alert_infraction_action': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'action',
+            'hr_infraction.mt_alert_infraction_noaction': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'noaction',
+        },
+    }
+
+    def _needaction_domain_get(self, cr, uid, context=None):
+
+        users_obj = self.pool.get('res.users')
+
+        domain = []
+        if users_obj.has_group(cr, uid, 'base.group_hr_manager'):
+            domain = [('state', '=', 'confirm')]
+
+        if len(domain) == 0:
+            return False
+
+        return domain
+
+    def unlink(self, cr, uid, ids, context=None):
+
+        for infraction in self.browse(cr, uid, ids, context=context):
+            if infraction.state not in ['draft']:
+                raise osv.except_osv(_('Error'),
+                                     _('Infractions that have progressed beyond "Draft" state may not be removed.'))
+
+        return super(hr_infraction, self).unlink(cr, uid, ids, context=context)
+
+    def onchange_category(self, cr, uid, ids, category_id, context=None):
+
+        res = {'value': {'name': False}}
+        if category_id:
+            category = self.pool.get(
+                'hr.infraction.category').browse(cr, uid, category_id,
+                                                 context=context)
+            res['value']['name'] = category.name
+        return res
+
+ACTION_TYPE_SELECTION = [
+    ('warning_verbal', 'Verbal Warning'),
+    ('warning_letter', 'Written Warning'),
+    ('transfer', 'Transfer'),
+    ('suspension', 'Suspension'),
+    ('dismissal', 'Dismissal'),
+]
+
+
+class hr_infraction_action(osv.Model):
+
+    _name = 'hr.infraction.action'
+    _description = 'Action Based on Infraction'
+
+    _columns = {
+        'infraction_id': fields.many2one('hr.infraction', 'Infraction', ondelete='cascade',
+                                         required=True, readonly=True),
+        'type': fields.selection(ACTION_TYPE_SELECTION, 'Type', required=True),
+        'memo': fields.text('Notes'),
+        'employee_id': fields.related('infraction_id', 'employee_id', type='many2one', store=True,
+                                      obj='hr.employee', string='Employee', readonly=True),
+        'warning_id': fields.many2one('hr.infraction.warning', 'Warning', readonly=True),
+        'transfer_id': fields.many2one('hr.department.transfer', 'Transfer', readonly=True),
+    }
+
+    _rec_name = 'type'
+
+    def unlink(self, cr, uid, ids, context=None):
+
+        for action in self.browse(cr, uid, ids, context=context):
+            if action.infraction_id.state not in ['draft']:
+                raise osv.except_osv(_('Error'),
+                                     _('Actions belonging to Infractions not in "Draft" state may not be removed.'))
+
+        return super(hr_infraction_action, self).unlink(cr, uid, ids, context=context)
+
+
+class hr_warning(osv.Model):
+
+    _name = 'hr.infraction.warning'
+    _description = 'Employee Warning'
+
+    _columns = {
+        'name': fields.char('Subject', size=256),
+        'date': fields.date('Date Issued'),
+        'type': fields.selection([('verbal', 'Verbal'), ('written', 'Written')], 'Type',
+                                 required=True),
+        'action_id': fields.many2one('hr.infraction.action', 'Action', ondelete='cascade',
+                                     readonly=True),
+        'infraction_id': fields.related('action_id', 'infraction_id', type='many2one',
+                                        obj='hr.infraction', string='Infraction', readonly=True),
+        'employee_id': fields.related('infraction_id', 'employee_id', type='many2one',
+                                      obj='hr.employee', string='Employee', readonly=True),
+    }
+
+    _defaults = {
+        'type': 'written',
+        'date': time.strftime(DEFAULT_SERVER_DATE_FORMAT),
+    }
+
+    def unlink(self, cr, uid, ids, context=None):
+
+        for warning in self.browse(cr, uid, ids, context=context):
+            if warning.action_id and warning.action_id.infraction_id.state not in ['draft']:
+                raise osv.except_osv(_('Error'),
+                                     _('Warnings attached to Infractions not in "Draft" state may not be removed.'))
+
+        return super(hr_warning, self).unlink(cr, uid, ids, context=context)
+
+
+class hr_employee(osv.Model):
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        'infraction_ids': fields.one2many('hr.infraction', 'employee_id', 'Infractions',
+                                          readonly=True),
+        'infraction_action_ids': fields.one2many('hr.infraction.action', 'employee_id',
+                                                 'Disciplinary Actions', readonly=True),
+    }

=== added file 'hr_infraction/hr_infraction_data.xml'
--- hr_infraction/hr_infraction_data.xml	1970-01-01 00:00:00 +0000
+++ hr_infraction/hr_infraction_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Infraction Category -->
+        <record id="infraction_category_care" model="hr.infraction.category">
+            <field name="code">CARE</field>
+            <field name="name">Improper care for Company equipment/materials</field>
+        </record>
+        <record id="infraction_category_life" model="hr.infraction.category">
+            <field name="code">ENDLIFE</field>
+            <field name="name">Intentional endangerment of life or property</field>
+        </record>
+        <record id="infraction_category_safety" model="hr.infraction.category">
+            <field name="code">SAFETY</field>
+            <field name="name">Disregard of safety and accident prevention rules</field>
+        </record>
+        <record id="infraction_category_rule" model="hr.infraction.category">
+            <field name="code">RULE</field>
+            <field name="name">Disregard for Company rules and regulations</field>
+        </record>
+        <record id="infraction_category_instruct" model="hr.infraction.category">
+            <field name="code">INSTRUCT</field>
+            <field name="name">Not following instructions</field>
+        </record>
+        <record id="infraction_category_abuse" model="hr.infraction.category">
+            <field name="code">ABUSE</field>
+            <field name="name">Abusive behaviour towards others</field>
+        </record>
+        <record id="infraction_category_harm" model="hr.infraction.category">
+            <field name="code">HARM</field>
+            <field name="name">Bodily harm to others</field>
+        </record>
+        <record id="infraction_category_theft" model="hr.infraction.category">
+            <field name="code">THEFT</field>
+            <field name="name">Theft of materials, equipment or property</field>
+        </record>
+        <record id="infraction_category_damage" model="hr.infraction.category">
+            <field name="code">DAMAGE</field>
+            <field name="name">Damage to Company materials, property or equipment</field>
+        </record>
+        <record id="infraction_category_intox" model="hr.infraction.category">
+            <field name="code">INTOX</field>
+            <field name="name">Reporting to work in a state of intoxication</field>
+        </record>
+        <record id="infraction_category_medical" model="hr.infraction.category">
+            <field name="code">MEDEXAM</field>
+            <field name="name">Refusal of lawful medical examination</field>
+        </record>
+        
+        <!-- Alert-related subtypes for messaging / Chatter -->
+        
+        <record id="mt_alert_infraction_confirmed" model="mail.message.subtype">
+            <field name="name">Infraction - Confirmed</field>
+            <field name="res_model">hr.infraction</field>
+            <field name="description">Infraction submitted</field>
+        </record>
+        
+        <record id="mt_alert_infraction_action" model="mail.message.subtype">
+            <field name="name">Infraction - Action Taken</field>
+            <field name="res_model">hr.infraction</field>
+            <field name="description">Action has been taken</field>
+        </record>
+        
+        <record id="mt_alert_infraction_noaction" model="mail.message.subtype">
+            <field name="name">Infraction - No Action Taken</field>
+            <field name="res_model">hr.infraction</field>
+            <field name="description">No further action necessary</field>
+        </record>
+        
+        <!-- Employment Termination Reason -->
+        <record id="term_dismissal" model="hr.employee.termination.reason">
+            <field name="name">Dismissal</field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_infraction/hr_infraction_view.xml'
--- hr_infraction/hr_infraction_view.xml	1970-01-01 00:00:00 +0000
+++ hr_infraction/hr_infraction_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Menu Parent -->
+        <menuitem id="menu_hr_infraction_root" name="Infractions" parent="hr.menu_hr_root" groups="base.group_hr_user" sequence="27"/>
+        
+        <!-- Infraction Categories -->
+        
+        <record id="hr_infraction_category_tree" model="ir.ui.view">
+            <field name="name">hr.infraction.category.tree</field>
+            <field name="model">hr.infraction.category</field>
+            <field name="arch" type="xml">
+                <tree string="Infraction Categories">
+                    <field name="name"/>
+                    <field name="code"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="hr_infraction_category_form" model="ir.ui.view">
+            <field name="name">hr.infraction.category.form</field>
+            <field name="model">hr.infraction.category</field>
+            <field name="arch" type="xml">
+                <form string="Infraction Category" version="7.0">
+                    <sheet>
+                        <field name="name"/>
+                        <field name="code"/>
+                    </sheet>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_hr_infraction_category" model="ir.actions.act_window">
+            <field name="name">Infraction Categories</field>
+            <field name="res_model">hr.infraction.category</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="open_hr_infraction_category"
+            id="menu_hr_infraction_category"
+            parent="hr.menu_hr_configuration"
+            sequence="5"/>
+        
+        <!-- Infraction -->
+        
+        <record id="hr_infraction_tree" model="ir.ui.view">
+            <field name="name">hr.infraction.tree</field>
+            <field name="model">hr.infraction</field>
+            <field name="arch" type="xml">
+                <tree string="Infractions">
+                    <field name="name"/>
+                    <field name="employee_id"/>
+                    <field name="date"/>
+                    <field name="category_id"/>
+                    <field name="state"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="hr_infraction_form" model="ir.ui.view">
+            <field name="name">hr.infraction.form</field>
+            <field name="model">hr.infraction</field>
+            <field name="arch" type="xml">
+                <form string="Infraction" version="7.0">
+                    <header>
+                        <button name="signal_confirm" type="workflow" states="draft" groups="base.group_hr_user" string="Confirm" class="oe_highlight"/>
+                        <button name="%(action_action_wizard)s" type="action" states="confirm,action" groups="base.group_hr_manager" string="Take Action" class="oe_highlight"/>
+                        <button name="signal_noaction" type="workflow" states="confirm" groups="base.group_hr_manager" string="No Further Action" class="oe_highlight"/>
+                        <field name="state" widget="statusbar"/>
+                    </header>
+                    <group>
+                        <group>
+                            <field name="category_id" widget="selection" on_change="onchange_category(category_id)"/>
+                            <field name="employee_id"/>
+                            <field name="date"/>
+                        </group>
+                        <group>
+                            <field name="name"/>
+                        </group>
+                        <group string="Description">
+                            <field name="memo" nolabel="1" placeholder="Describe the incident here..."/>
+                        </group>
+                        <group string="Action(s)">
+                            <field name="action_ids" nolabel="1">
+                                <tree string="Actions Taken">
+                                    <field name="type"/>
+                                    <field name="memo"/>
+                                </tree>
+                            </field>
+                        </group>
+                    </group>
+                    <newline/>
+                    <div class="oe_chatter">
+                        <field name="message_follower_ids" widget="mail_followers"/>
+                        <field name="message_ids" widget="mail_thread"/>
+                    </div>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_hr_infraction" model="ir.actions.act_window">
+            <field name="name">Infractions</field>
+            <field name="res_model">hr.infraction</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem id="menu_hr_infraction"
+            action="open_hr_infraction"
+            parent="menu_hr_infraction_root"
+            sequence="5"/>
+        
+        <!-- Warnings -->
+        
+        <record id="hr_infraction_warning_tree" model="ir.ui.view">
+            <field name="name">hr.infraction.warning.tree</field>
+            <field name="model">hr.infraction.warning</field>
+            <field name="arch" type="xml">
+                <tree string="Warnings">
+                    <field name="employee_id"/>
+                    <field name="name"/>
+                    <field name="date"/>
+                    <field name="type"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="hr_infraction_warning_form" model="ir.ui.view">
+            <field name="name">hr.infraction.warning.form</field>
+            <field name="model">hr.infraction.warning</field>
+            <field name="arch" type="xml">
+                <form string="Warning" version="7.0">
+                    <sheet>
+                        <group>
+                            <group>
+                                <field name="name"/>
+                                <field name="date"/>
+                                <field name="employee_id"/>
+                            </group>
+                            <group>
+                                <field name="type"/>
+                                <field name="action_id" invisible="1"/>
+                                <field name="infraction_id"/>
+                            </group>
+                        </group>
+                    </sheet>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_hr_infraction_warning" model="ir.actions.act_window">
+            <field name="name">Warnings</field>
+            <field name="res_model">hr.infraction.warning</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem id="menu_hr_infraction_warning"
+            action="open_hr_infraction_warning"
+            parent="menu_hr_infraction_root"
+            sequence="10"/>
+        
+        <!-- Infraction Actions -->
+        
+        <record id="hr_infraction_action_tree" model="ir.ui.view">
+            <field name="name">hr.infraction.action.tree</field>
+            <field name="model">hr.infraction.action</field>
+            <field name="arch" type="xml">
+                <tree string="Infraction Actions">
+                    <field name="employee_id"/>
+                    <field name="infraction_id"/>
+                    <field name="type"/>
+                    <field name="memo"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="hr_infraction_action_form" model="ir.ui.view">
+            <field name="name">hr.infraction.action.form</field>
+            <field name="model">hr.infraction.action</field>
+            <field name="arch" type="xml">
+                <form string="Infraction Action" version="7.0">
+                    <sheet>
+                        <group>
+                            <group>
+                                <field name="employee_id"/>
+                                <field name="infraction_id"/>
+                                <field name="warning_id" attrs="{'invisible': [('type','not in',['warning_verbal','warning_letter'])]}"/>
+                                <field name="transfer_id" attrs="{'invisible': [('type','!=','transfer')]}"/>
+                            </group>
+                            <group>
+                                <field name="type"/>
+                            </group>
+                        </group>
+                        <separator string="Notes"/>
+                        <field name="memo" nolabel="1"/>
+                    </sheet>
+                </form>
+            </field>
+        </record>
+        
+        <record id="open_hr_infraction_action" model="ir.actions.act_window">
+            <field name="name">Actions</field>
+            <field name="res_model">hr.infraction.action</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem id="menu_hr_infraction_action"
+            action="open_hr_infraction_action"
+            parent="menu_hr_infraction_root"
+            sequence="15"/>
+        
+        <!-- Employee Form -->
+        
+        <record id="view_employee_form" model="ir.ui.view">
+            <field name="name">hr.employee.form.inherit.infraction</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr.view_employee_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//page[@string='HR Settings']" position="after">
+                        <page string="Disciplinary Information">
+                            <group>
+                                <group string="Infractions">
+                                    <field name="infraction_ids" nolabel="1">
+                                        <tree string="Infractions">
+                                            <field name="date"/>
+                                            <field name="name"/>
+                                            <field name="state" invisible="1"/>
+                                        </tree>
+                                    </field>
+                                </group>
+                                <group string="Disciplinary Actions">
+                                    <field name="infraction_action_ids" nolabel="1">
+                                        <tree string="Actions">
+                                            <field name="type"/>
+                                            <field name="infraction_id"/>
+                                        </tree>
+                                    </field>
+                                </group>
+                            </group>
+                        </page>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_infraction/hr_infraction_workflow.xml'
--- hr_infraction/hr_infraction_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_infraction/hr_infraction_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+    
+        <!-- Workflow Definition -->
+        <record id="wkf_employee_infraction" model="workflow">
+            <field name="name">hr.infraction.basic</field>
+            <field name="osv">hr.infraction</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (Stages) -->
+        
+        <record id="act_draft" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_infraction"/>
+            <field name="name">draft</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'draft'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_confirm" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_infraction"/>
+            <field name="name">confirm</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'confirm'})</field>
+        </record>
+        
+        <record id="act_action" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_infraction"/>
+            <field name="name">action</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'action'})</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        <record id="act_noaction" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_employee_infraction"/>
+            <field name="name">noaction</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'noaction'})</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="draft2confirm" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_confirm"/>
+            <field name="signal">signal_confirm</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="confirm2action" model="workflow.transition">
+            <field name="act_from" ref="act_confirm"/>
+            <field name="act_to" ref="act_action"/>
+            <field name="signal">signal_action</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="confirm2noaction" model="workflow.transition">
+            <field name="act_from" ref="act_confirm"/>
+            <field name="act_to" ref="act_noaction"/>
+            <field name="signal">signal_noaction</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_infraction/security'
=== added file 'hr_infraction/security/ir.model.access.csv'
--- hr_infraction/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_infraction/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,12 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_infraction_user,access_hr_infraction,model_hr_infraction,base.group_hr_user,1,1,1,1
+access_hr_infraction_action_user,access_hr_infraction_action,model_hr_infraction_action,base.group_hr_user,1,0,0,0
+access_hr_infraction_action_manager,access_hr_infraction_action,model_hr_infraction_action,base.group_hr_manager,1,1,1,1
+access_hr_infraction_warning_user,access_hr_infraction_warning,model_hr_infraction_warning,base.group_hr_user,1,0,0,0
+access_hr_infraction_warning_manager,access_hr_infraction_warning,model_hr_infraction_warning,base.group_hr_manager,1,1,1,1
+access_hr_infraction_category_user,access_hr_infraction_category,model_hr_infraction_category,base.group_hr_user,1,0,0,0
+access_hr_infraction_category_manager,access_hr_infraction_category,model_hr_infraction_category,base.group_hr_manager,1,1,1,1
+access_hr_infraction_pm,access_hr_infraction,hr_infraction.model_hr_infraction,hr_security.group_payroll_manager,1,0,0,0
+access_hr_infraction_action_pm,access_hr_infraction_action,hr_infraction.model_hr_infraction_action,hr_security.group_payroll_manager,1,0,0,0
+access_hr_infraction_warning_pm,access_hr_infraction_warning,hr_infraction.model_hr_infraction_warning,hr_security.group_payroll_manager,1,0,0,0
+access_hr_infraction_category_pm,access_hr_infraction_category,hr_infraction.model_hr_infraction_category,hr_security.group_payroll_manager,1,0,0,0

=== added directory 'hr_infraction/wizard'
=== added file 'hr_infraction/wizard/__init__.py'
--- hr_infraction/wizard/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_infraction/wizard/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import action

=== added file 'hr_infraction/wizard/action.py'
--- hr_infraction/wizard/action.py	1970-01-01 00:00:00 +0000
+++ hr_infraction/wizard/action.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,167 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp import netsvc
+from openerp.osv import fields, osv
+
+from openerp.addons.hr_infraction.hr_infraction import ACTION_TYPE_SELECTION
+
+
+class action_wizard(osv.TransientModel):
+
+    _name = 'hr.infraction.action.wizard'
+    _description = 'Choice of Actions for Infraction'
+
+    _columns = {
+        'action_type': fields.selection(ACTION_TYPE_SELECTION, 'Action', required=True),
+        'memo': fields.text('Notes'),
+        'new_job_id': fields.many2one('hr.job', 'New Job'),
+        'xfer_effective_date': fields.date('Effective Date'),
+        'effective_date': fields.date('Effective Date'),
+    }
+
+    def create_action(self, cr, uid, ids, context=None):
+
+        if context == None:
+            context = {}
+        infraction_id = context.get('active_id', False)
+        if not infraction_id:
+            return False
+        data = self.read(cr, uid, ids[0], context=context)
+
+        vals = {
+            'infraction_id': infraction_id,
+            'type': data['action_type'],
+            'memo': data.get('memo', False),
+        }
+        action_id = self.pool.get('hr.infraction.action').create(
+            cr, uid, vals, context=context)
+
+        # Update state of infraction, if not already done so
+        #
+        infraction_obj = self.pool.get('hr.infraction')
+        infraction_data = infraction_obj.read(
+            cr, uid, infraction_id, ['employee_id', 'state'],
+            context=context)
+        if infraction_data['state'] == 'confirm':
+            netsvc.LocalService(
+                'workflow').trg_validate(uid, 'hr.infraction', infraction_id,
+                                         'signal_action', cr)
+
+        infraa_obj = self.pool.get('hr.infraction.action')
+        imd_obj = self.pool.get('ir.model.data')
+        iaa_obj = self.pool.get('ir.actions.act_window')
+
+        # If the action is a warning create the appropriate record, reference it from the action,
+        # and pull it up in the view (in case the user needs to make any changes.
+        #
+        if data['action_type'] in ['warning_verbal', 'warning_letter']:
+            vals = {
+                'name': (data['action_type'] == 'warning_verbal' and 'Verbal' or 'Written') + ' Warning',
+                'type': data['action_type'] == 'warning_verbal' and 'verbal' or 'written',
+                'action_id': action_id,
+            }
+            warning_id = self.pool.get('hr.infraction.warning').create(
+                cr, uid, vals, context=context)
+            infraa_obj.write(cr, uid, action_id, {
+                             'warning_id': warning_id}, context=context)
+
+            res_model, res_id = imd_obj.get_object_reference(
+                cr, uid, 'hr_infraction',
+                'open_hr_infraction_warning')
+            dict_act_window = iaa_obj.read(cr, uid, res_id, [])
+            dict_act_window['view_mode'] = 'form,tree'
+            dict_act_window['domain'] = [('id', '=', warning_id)]
+            return dict_act_window
+
+        # If the action is a departmental transfer create the appropriate record, reference it from
+        # the action, and pull it up in the view (in case the user needs to make any changes.
+        #
+        elif data['action_type'] == 'transfer':
+            xfer_obj = self.pool.get('hr.department.transfer')
+            ee = self.pool.get(
+                'hr.employee').browse(cr, uid, infraction_data['employee_id'][0],
+                                      context=context)
+            _tmp = xfer_obj.onchange_employee(
+                cr, uid, None, ee.id, context=context)
+            vals = {
+                'employee_id': ee.id,
+                'src_id': _tmp['value']['src_id'],
+                'dst_id': data['new_job_id'][0],
+                'src_contract_id': _tmp['value']['src_contract_id'],
+                'date': data['xfer_effective_date'],
+            }
+            xfer_id = xfer_obj.create(cr, uid, vals, context=context)
+            infraa_obj.write(cr, uid, action_id, {
+                             'transfer_id': xfer_id}, context=context)
+
+            res_model, res_id = imd_obj.get_object_reference(
+                cr, uid, 'hr_transfer',
+                'open_hr_department_transfer')
+            dict_act_window = iaa_obj.read(cr, uid, res_id, [])
+            dict_act_window['view_mode'] = 'form,tree'
+            dict_act_window['domain'] = [('id', '=', xfer_id)]
+            return dict_act_window
+
+        # The action is dismissal. Begin the termination process.
+        #
+        elif data['action_type'] == 'dismissal':
+            term_obj = self.pool.get('hr.employee.termination')
+            wkf = netsvc.LocalService('workflow')
+            ee = self.pool.get(
+                'hr.employee').browse(cr, uid, infraction_data['employee_id'][0],
+                                      context=context)
+
+            # We must create the employment termination object before we set
+            # the contract state to 'done'.
+            res_model, res_id = imd_obj.get_object_reference(
+                cr, uid, 'hr_infraction', 'term_dismissal')
+            vals = {
+                'employee_id': ee.id,
+                'name': data['effective_date'],
+                'reason_id': res_id,
+            }
+            term_id = term_obj.create(cr, uid, vals, context=context)
+            infraa_obj.write(cr, uid, action_id, {
+                             'termination_id': term_id}, context=context)
+
+            # End any open contracts
+            for contract in ee.contract_ids:
+                if contract.state not in ['done']:
+                    wkf.trg_validate(
+                        uid, 'hr.contract', contract.id, 'signal_pending_done', cr)
+
+            # Set employee state to pending deactivation
+            wkf.trg_validate(
+                uid, 'hr.employee', ee.id, 'signal_pending_inactive', cr)
+
+            # Trigger confirmation of termination record
+            wkf.trg_validate(
+                uid, 'hr.employee.termination', term_id, 'signal_confirmed', cr)
+
+            res_model, res_id = imd_obj.get_object_reference(
+                cr, uid, 'hr_employee_state',
+                'open_hr_employee_termination')
+            dict_act_window = iaa_obj.read(cr, uid, res_id, [])
+            dict_act_window['domain'] = [('id', '=', term_id)]
+            return dict_act_window
+
+        return True

=== added file 'hr_infraction/wizard/action.xml'
--- hr_infraction/wizard/action.xml	1970-01-01 00:00:00 +0000
+++ hr_infraction/wizard/action.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <record id="hr_infraction_action_wizard_form" model="ir.ui.view">
+            <field name="name">hr.infraction.action.wizard.form</field>
+            <field name="model">hr.infraction.action.wizard</field>
+            <field name="arch" type="xml">
+                <form string="Choose Action" version="7.0">
+                    <header>
+                        <button name="create_action" type="object" string="Take Action" class="oe_highlight"/>
+                    </header>
+                    <div>
+                        <h2>Please choose the appropriate action from the list below</h2>
+                    </div>
+                    <group>
+                        <group>
+                            <field name="action_type"/>
+                        </group>
+                        <group></group>
+                    </group>
+                    <group string="Departmental Transfer" attrs="{'invisible': [('action_type','!=','transfer')]}">
+                        <group>
+                            <field name="new_job_id" widget="selection"/>
+                            <field name="xfer_effective_date"/>
+                        </group>
+                        <group></group>
+                    </group>
+                    <group string="Dismissal" attrs="{'invisible': [('action_type','!=','dismissal')]}">
+                        <group>
+                            <field name="effective_date"/>
+                        </group>
+                        <group></group>
+                    </group>
+                    <separator string="Notes"/>
+                    <field name="memo" nolabel="1" colspan="4"/>
+                </form>
+            </field>
+        </record>
+
+        <record id="action_action_wizard" model="ir.actions.act_window">
+            <field name="name">Infraction Action</field>
+            <field name="res_model">hr.infraction.action.wizard</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">form</field>
+            <field name="view_id" ref="hr_infraction_action_wizard_form"/>
+            <field name="target">new</field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_job_categories'
=== added file 'hr_job_categories/__init__.py'
--- hr_job_categories/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_job_categories/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr

=== added file 'hr_job_categories/__openerp__.py'
--- hr_job_categories/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_job_categories/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,51 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Job Categories',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Attach Categories (Tags) to Employees Based on Job Position
+===========================================================
+
+This module is useful for tagging employees based on their job positions. For example,
+all Supervisors could be attached to the Supervisors category. Define which categries
+a job belongs to in the configuration for the job. When an employee is assigned a particular
+job the categories attached to that job will be attached to the employee record as well.
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_job_categories/hr.py'
--- hr_job_categories/hr.py	1970-01-01 00:00:00 +0000
+++ hr_job_categories/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,118 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+
+import logging
+_l = logging.getLogger(__name__)
+
+
+class hr_job(osv.Model):
+
+    _inherit = 'hr.job'
+
+    _columns = {
+        'category_ids': fields.many2many('hr.employee.category', 'job_category_rel', 'job_id',
+                                         'category_id', 'Associated Tags'),
+    }
+
+
+class hr_contract(osv.Model):
+
+    _name = 'hr.contract'
+    _inherit = 'hr.contract'
+
+    def _remove_tags(self, cr, uid, employee_id, job_id, context=None):
+
+        if not employee_id or not job_id:
+            return
+
+        ee_obj = self.pool.get('hr.employee')
+        eedata = ee_obj.read(
+            cr, uid, employee_id, ['category_ids'], context=context)
+        job = self.pool.get('hr.job').browse(cr, uid, job_id, context=context)
+        _l.warning('remv: eedata: %s', eedata)
+        for tag in job.category_ids:
+            if tag.id in eedata['category_ids']:
+                ee_obj.write(cr, uid, employee_id, {
+                             'category_ids': [(3, tag.id)]}, context=context)
+        return
+
+    def _tag_employees(self, cr, uid, employee_id, job_id, context=None):
+
+        if not employee_id or not job_id:
+            return
+
+        ee_obj = self.pool.get('hr.employee')
+        eedata = ee_obj.read(
+            cr, uid, employee_id, ['category_ids'], context=context)
+        job = self.pool.get('hr.job').browse(cr, uid, job_id, context=context)
+        _l.warning('tag: eedata: %s', eedata)
+        for tag in job.category_ids:
+            _l.warning('tag: name,id: %s,%s', tag.name, tag.id)
+            if tag.id not in eedata['category_ids']:
+                _l.warning('tag: write()')
+                ee_obj.write(cr, uid, employee_id, {
+                             'category_ids': [(4, tag.id)]}, context=context)
+        return
+
+    def create(self, cr, uid, vals, context=None):
+
+        res = super(hr_contract, self).create(cr, uid, vals, context=context)
+
+        self._tag_employees(
+            cr, uid, vals.get('employee_id', False), vals.get('job_id', False),
+            context)
+
+        return res
+
+    def write(self, cr, uid, ids, vals, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        prev_data = self.read(cr, uid, ids, ['job_id'], context=context)
+        _l.warning('prev_data: %s', prev_data)
+
+        res = super(hr_contract, self).write(
+            cr, uid, ids, vals, context=context)
+
+        # Go through each record and delete tags associated with the previous job, then
+        # add the tags of the new job.
+        #
+        for contract in self.browse(cr, uid, ids, context=context):
+            for data in prev_data:
+                if data['id'] == contract.id:
+                    if not vals.get('job_id', False) or data['job_id'][0] != vals['job_id']:
+                        prev_job_id = data.get(
+                            'job_id', False) and data['job_id'][0] or False
+                        _l.warning(
+                            'prev Job, new job: %s, %s', prev_job_id, vals.get('job_id', False))
+                        self._remove_tags(
+                            cr, uid, contract.employee_id.id, prev_job_id,
+                            context=context)
+                        if vals.get('job_id', False):
+                            self._tag_employees(
+                                cr, uid, contract.employee_id.id, contract.job_id.id,
+                                context=context)
+
+        return res

=== added file 'hr_job_categories/hr_view.xml'
--- hr_job_categories/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_job_categories/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="view_job_form" model="ir.ui.view">
+            <field name="name">hr.job.categories</field>
+            <field name="model">hr.job</field>
+            <field name="inherit_id" ref="hr.view_hr_job_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//div[@class='oe_title']" position="inside">
+                    <label for="category_ids" class="oe_edit_only" groups="base.group_hr_manager"/>
+                    <field name="category_ids" widget="many2many_tags" placeholder="e.g. Part Time" groups="base.group_hr_manager"/>
+                </xpath>
+            </field>
+        </record>
+    
+    </data>
+</openerp>

=== added directory 'hr_job_hierarchy'
=== added file 'hr_job_hierarchy/__init__.py'
--- hr_job_hierarchy/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_job_hierarchy/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr

=== added file 'hr_job_hierarchy/__openerp__.py'
--- hr_job_hierarchy/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_job_hierarchy/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,53 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Job Hierarchy',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Define Hierarchy of Jobs
+========================
+
+    1. Define parent/child relationship for jobs, which is useful for
+       determining supervisor/subordinate relationships.
+    2. Provide a knob in Job configuration to specify if the person with that
+       job should be considered the Department manager.
+    3. Automatically set the manager of a department based on this knob in job configuration.
+    4. Automatically set an employee's manager from the department's manager.
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_job_hierarchy/hr.py'
--- hr_job_hierarchy/hr.py	1970-01-01 00:00:00 +0000
+++ hr_job_hierarchy/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,209 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
+
+import logging
+_l = logging.getLogger(__name__)
+
+
+class hr_job(osv.Model):
+
+    _name = 'hr.job'
+    _inherit = 'hr.job'
+
+    def _get_all_child_ids(self, cr, uid, ids, field_name, arg, context=None):
+        result = dict.fromkeys(ids)
+        for i in ids:
+            result[i] = self.search(
+                cr, uid, [('parent_id', 'child_of', i)], context=context)
+
+        return result
+
+    _columns = {
+        'department_manager': fields.boolean('Department Manager'),
+        'parent_id': fields.many2one('hr.job', 'Immediate Superior', ondelete='cascade'),
+        'child_ids': fields.one2many('hr.job', 'parent_id', 'Immediate Subordinates'),
+        'all_child_ids': fields.function(_get_all_child_ids, type='many2many', relation='hr.job'),
+        'parent_left': fields.integer('Left Parent', select=1),
+        'parent_right': fields.integer('Right Parent', select=1),
+    }
+
+    _parent_name = "parent_id"
+    _parent_store = True
+    _parent_order = 'name'
+    _order = 'parent_left'
+
+    def _check_recursion(self, cr, uid, ids, context=None):
+
+        # Copied from product.category
+        # This is a brute-force approach to the problem, but should be good enough.
+        #
+        level = 100
+        while len(ids):
+            cr.execute(
+                'select distinct parent_id from hr_job where id IN %s', (tuple(ids),))
+            ids = filter(None, map(lambda x: x[0], cr.fetchall()))
+            if not level:
+                return False
+            level -= 1
+        return True
+
+    _constraints = [
+        (_check_recursion,
+         _('Error!\nYou cannot create recursive jobs.'), ['parent_id']),
+    ]
+
+    def write(self, cr, uid, ids, vals, context=None):
+
+        res = super(hr_job, self).write(cr, uid, ids, vals, context=None)
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        dept_obj = self.pool.get('hr.department')
+        if vals.get('department_manager', False):
+            for di in ids:
+                job = self.browse(cr, uid, di, context=context)
+                dept_id = False
+                if vals.get('department_id', False):
+                    dept_id = vals['department_id']
+                else:
+                    dept_id = job.department_id.id
+                employee_id = False
+                for ee in job.employee_ids:
+                    employee_id = ee.id
+                if employee_id:
+                    dept_obj.write(
+                        cr, uid, dept_id, {'manager_id': employee_id}, context=context)
+        elif vals.get('department_id', False):
+            for di in ids:
+                job = self.browse(cr, uid, di, context=context)
+                if job.department_manager:
+                    employee_id = False
+                    for ee in job.employee_ids:
+                        employee_id = ee.id
+                    dept_obj.write(cr, uid, vals['department_id'], {
+                                   'manager_id': employee_id}, context=context)
+        elif vals.get('parent_id', False):
+            ee_obj = self.pool.get('hr.employee')
+            parent_job = self.browse(
+                cr, uid, vals['parent_id'], context=context)
+            parent_id = False
+            for ee in parent_job.employee_ids:
+                parent_id = ee.id
+            for job in self.browse(cr, uid, ids, context=context):
+                for ee in job.employee_ids:
+                    ee_obj.write(
+                        cr, uid, ee.id, {'parent_id': parent_id}, context=context)
+
+        return res
+
+
+class hr_contract(osv.Model):
+
+    _name = 'hr.contract'
+    _inherit = 'hr.contract'
+
+    def create(self, cr, uid, vals, context=None):
+
+        res = super(hr_contract, self).create(cr, uid, vals, context=context)
+
+        if not vals.get('job_id', False):
+            return res
+
+        ee_obj = self.pool.get('hr.employee')
+        job = self.pool.get('hr.job').browse(
+            cr, uid, vals['job_id'], context=context)
+
+        # Write the employee's manager
+        if job and job.parent_id:
+            parent_id = False
+            for ee in job.parent_id.employee_ids:
+                parent_id = ee.id
+            if parent_id and vals.get('employee_id'):
+                ee_obj.write(
+                    cr, uid, vals['employee_id'], {'parent_id': parent_id},
+                    context=context)
+
+        # Write any employees with jobs that are immediate descendants of this
+        # job
+        if job:
+            job_child_ids = []
+            [job_child_ids.append(child.id) for child in job.child_ids]
+            if len(job_child_ids) > 0:
+                ee_ids = ee_obj.search(
+                    cr, uid, [('job_id', 'in', job_child_ids)])
+                if len(ee_ids) > 0:
+                    parent_id = False
+                    for ee in job.employee_ids:
+                        parent_id = ee.id
+                    if parent_id:
+                        ee_obj.write(
+                            cr, uid, ee_ids, {'parent_id': parent_id}, context=context)
+
+        return res
+
+    def write(self, cr, uid, ids, vals, context=None):
+
+        res = super(hr_contract, self).write(
+            cr, uid, ids, vals, context=context)
+
+        if not vals.get('job_id', False):
+            return res
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        ee_obj = self.pool.get('hr.employee')
+
+        job = self.pool.get('hr.job').browse(
+            cr, uid, vals['job_id'], context=context)
+
+        # Write the employee's manager
+        if job and job.parent_id:
+            parent_id = False
+            for ee in job.parent_id.employee_ids:
+                parent_id = ee.id
+            if parent_id:
+                for contract in self.browse(cr, uid, ids, context=context):
+                    ee_obj.write(cr, uid, contract.employee_id.id, {
+                                 'parent_id': parent_id}, context=context)
+
+        # Write any employees with jobs that are immediate descendants of this
+        # job
+        if job:
+            job_child_ids = []
+            [job_child_ids.append(child.id) for child in job.child_ids]
+            if len(job_child_ids) > 0:
+                ee_ids = ee_obj.search(
+                    cr, uid, [('job_id', 'in', job_child_ids),
+                              ('active', '=', True)])
+                if len(ee_ids) > 0:
+                    parent_id = False
+                    for ee in job.employee_ids:
+                        parent_id = ee.id
+                    if parent_id:
+                        ee_obj.write(
+                            cr, uid, ee_ids, {'parent_id': parent_id}, context=context)
+
+        return res

=== added file 'hr_job_hierarchy/hr_view.xml'
--- hr_job_hierarchy/hr_view.xml	1970-01-01 00:00:00 +0000
+++ hr_job_hierarchy/hr_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <!-- Job Form -->
+        
+        <record id="job_form_view" model="ir.ui.view">
+            <field name="name">hr.job.form.hierarchy</field>
+            <field name="model">hr.job</field>
+            <field name="inherit_id" ref="hr.view_hr_job_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='department_id']" position="after">
+                        <field name="parent_id"/>
+                        <field name="department_manager"/>
+                    </xpath>
+                    <xpath expr="//sheet" position="inside">
+                        <group string="Immediate Subordinates">
+                            <field name="child_ids" nolabel="1"/>
+                        </group>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_labour_recruitment'
=== added file 'hr_labour_recruitment/__init__.py'
--- hr_labour_recruitment/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_labour_recruitment/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_recruitment

=== added file 'hr_labour_recruitment/__openerp__.py'
--- hr_labour_recruitment/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_labour_recruitment/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,52 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'New Employee Recruitment and Personnel Requests',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Recruitment of New Employees
+============================
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr_contract',
+        'hr_contract_state',
+        'hr_recruitment',
+        'hr_security',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'hr_recruitment_data.xml',
+        'hr_recruitment_view.xml',
+        'hr_recruitment_workflow.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_labour_recruitment/hr_recruitment.py'
--- hr_labour_recruitment/hr_recruitment.py	1970-01-01 00:00:00 +0000
+++ hr_labour_recruitment/hr_recruitment.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,291 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp.osv import fields, osv
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DATEFORMAT
+from openerp.tools.translate import _
+
+
+class hr_job(osv.Model):
+
+    _name = 'hr.job'
+    _inherit = 'hr.job'
+
+    _columns = {
+        'max_employees': fields.integer('Maximum Number of Employees'),
+        'max_employees_fuzz': fields.integer('Expected Turnover',
+                                             help="Recruitment module will allow you to \
+                                                  create this number of additional applicants and \
+                                                  contracts above the maximum value. Use this \
+                                                  number as a buffer to have additional \
+                                                  employees on hand to cover employee turnover."),
+    }
+
+    # Do not write negative values for no. of recruitment
+    def write(self, cr, uid, ids, vals, context=None):
+
+        value = vals.get('no_of_recruitment', False)
+        if value and value < 0:
+            vals['no_of_recruitment'] = 0
+
+        return super(hr_job, self).write(cr, uid, ids, vals, context=context)
+
+
+class hr_applicant(osv.Model):
+
+    _name = 'hr.applicant'
+    _inherit = 'hr.applicant'
+
+    def create(self, cr, uid, vals, context=None):
+
+        if vals.get('job_id', False):
+            data = self.pool.get('hr.job').read(cr, uid, vals['job_id'],
+                                                ['max_employees', 'no_of_employee', 'state',
+                                                 'max_employees_fuzz'],
+                                                context=context)
+            if data.get('state', False):
+                if data['state'] != 'recruit' and int(data['no_of_employee']) >= (int(data['max_employees']) + data['max_employees_fuzz']):
+                    raise osv.except_osv(_('Job not open for recruitment!'),
+                                         _('You may not register applicants for jobs that are not recruiting.'))
+
+        return super(hr_applicant, self).create(cr, uid, vals, context=context)
+
+
+class hr_contract(osv.Model):
+
+    _name = 'hr.contract'
+    _inherit = 'hr.contract'
+
+    def _get_job_from_applicant(self, cr, uid, context=None):
+        """If the applicant went through recruitment get the job id from there."""
+
+        res = False
+        if context != None:
+            ee_ids = context.get('search_default_employee_id', False)
+            if ee_ids and len(ee_ids) > 0:
+                # If this is the first contract try to obtain job position from
+                # application
+                if len(self.search(cr, uid, [('employee_id', 'in', ee_ids)], context=context)) > 0:
+                    return res
+                applicant_obj = self.pool.get('hr.applicant')
+                applicant_ids = applicant_obj.search(
+                    cr, uid, [('emp_id', '=', ee_ids[0])], context=context)
+                if len(applicant_ids) > 0:
+                    data = applicant_obj.read(
+                        cr, uid, applicant_ids[0], ['job_id'], context=context)
+                    res = data['job_id'][0]
+
+        return res
+
+    _defaults = {
+        'job_id': _get_job_from_applicant,
+    }
+
+    def create(self, cr, uid, vals, context=None):
+
+        # If the contract is for an employee with a pre-existing contract for
+        # the same job, then bypass checks.
+        employee_id = vals.get('employee_id', False)
+        if employee_id:
+            contract_ids = self.search(cr, uid, [
+                ('employee_id', '=', employee_id),
+                ('state', 'not in', [
+                    'draft', 'done']),
+            ],
+                context=context)
+            for contract in self.browse(cr, uid, contract_ids, context=context):
+                if vals.get('job_id', False) == contract.job_id.id:
+                    return super(hr_contract, self).create(cr, uid, vals, context=context)
+
+        # 1. Verify job is in recruitment
+        if vals.get('job_id', False):
+            data = self.pool.get('hr.job').read(cr, uid, vals['job_id'],
+                                                ['name', 'max_employees', 'max_employees_fuzz',
+                                                    'no_of_employee', 'state'],
+                                                context=context)
+            if data.get('state', False):
+                if data['state'] != 'recruit' and int(data['no_of_employee']) >= (int(data['max_employees']) + data['max_employees_fuzz']):
+                    raise osv.except_osv(
+                        _('The Job "%s" is not in recruitment!') % (
+                            data['name']),
+                        _('You may not create contracts for jobs that are not in recruitment state.'))
+
+        # 2. Verify that the number of open contracts < total expected
+        # employees
+        if vals.get('job_id', False):
+            contract_ids = self.search(cr, uid, [
+                ('job_id', '=', vals[
+                    'job_id']),
+                ('state', 'not in', ['done']),
+            ],
+                context=context)
+
+            data = self.pool.get('hr.job').read(cr, uid, vals['job_id'],
+                                                ['name', 'expected_employees',
+                                                    'max_employees', 'max_employees_fuzz'],
+                                                context=context)
+            expected_employees = data.get(
+                'expected_employees', False) and data['expected_employees'] or 0
+            max_employees = data.get(
+                'max_employees', False) and data['max_employees'] or 0
+            max_employees_fuzz = data.get(
+                'max_employees_fuzz', False) and data['max_employees_fuzz'] or 0
+
+            if len(contract_ids) >= max(expected_employees, max_employees + max_employees_fuzz):
+                raise osv.except_osv(
+                    _('Maximum Number of Employees Exceeded!'),
+                    _('The maximum number of employees for "%s" has been exceeded.') % (data['name']))
+
+        return super(hr_contract, self).create(cr, uid, vals, context=context)
+
+
+class hr_recruitment_request(osv.Model):
+
+    _name = 'hr.recruitment.request'
+    _description = 'Request for recruitment of additional personnel'
+    _inherit = ['mail.thread', 'ir.needaction_mixin']
+
+    _columns = {
+        'name': fields.char('Description', size=64),
+        'user_id': fields.many2one('res.users', 'Requesting User', required=True),
+        'department_id': fields.many2one('hr.department', 'Department'),
+        'job_id': fields.many2one('hr.job', 'Job', required=True),
+        'number': fields.integer('Number to Recruit'),
+        'current_number': fields.related('job_id', 'no_of_employee', type='integer', string="Current Number of Employees", readonly=True),
+        'max_number': fields.related('job_id', 'max_employees', type='integer', string="Maximum Number of Employees", readonly=True),
+        'reason': fields.text('Reason for Request'),
+        'state': fields.selection([('draft', 'Draft'),
+                                   ('confirm', 'Confirmed'),
+                                   ('exception', 'Exception'),
+                                   ('recruitment', 'In Recruitment'),
+                                   ('decline', 'Declined'),
+                                   ('done', 'Done'),
+                                   ('cancel', 'Cancelled'),
+                                   ],
+                                  'State', readonly=True),
+    }
+
+    _order = 'department_id, job_id'
+
+    _defaults = {
+        'number': 1,
+        'user_id': lambda self, cr, uid, context=None: uid,
+    }
+
+    _track = {
+        'state': {
+            'hr_labour_recruitment.mt_alert_request_confirmed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'confirm',
+            'hr_labour_recruitment.mt_alert_request_exception': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'exception',
+            'hr_labour_recruitment.mt_alert_request_approved': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'recruitment',
+            'hr_labour_recruitment.mt_alert_request_declined': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'decline',
+        },
+    }
+
+    def onchange_job(self, cr, uid, ids, job_id, context=None):
+
+        res = {'value': {'deparment_id': False, 'name': False}}
+        if job_id:
+            data = self.pool.get('hr.job').read(
+                cr, uid, job_id, ['name', 'department_id'], context=context)
+            if data.get('department_id', False):
+                res['value']['department_id'] = data['department_id'][0]
+            res['value']['name'] = 'Personnel Request: ' + str(data['name'])
+
+        return res
+
+    def _needaction_domain_get(self, cr, uid, context=None):
+
+        users_obj = self.pool.get('res.users')
+
+        domain = []
+        has_prev_domain = False
+        if users_obj.has_group(cr, uid, 'base.group_hr_manager'):
+            domain = [('state', '=', 'recruitment')]
+            has_prev_domain = True
+        if users_obj.has_group(cr, uid, 'hr_security.group_hr_director'):
+            if has_prev_domain:
+                domain = ['|'] + domain
+            domain = domain + [('state', 'in', ['confirm', 'exception'])]
+
+        if len(domain) == 0:
+            return False
+
+        return domain
+
+    def condition_exception(self, cr, uid, ids, context=None):
+
+        for req in self.browse(cr, uid, ids, context=context):
+            if req.number + req.job_id.expected_employees > req.job_id.max_employees:
+                return True
+
+        return False
+
+    def _state(self, cr, uid, ids, state, context=None):
+
+        job_obj = self.pool.get('hr.job')
+
+        for req in self.browse(cr, uid, ids, context=context):
+
+            if state == 'recruitment':
+                job_obj.write(cr, uid, req.job_id.id, {
+                              'no_of_recruitment': req.number}, context=context)
+                job_obj.job_recruitement(cr, uid, [req.job_id.id])
+            elif state in ['done', 'cancel']:
+                job_obj.job_open(cr, uid, [req.job_id.id])
+
+            self.write(cr, uid, req.id, {'state': state}, context=context)
+
+        return True
+
+    def _state_subscribe_users(self, cr, uid, ids, state, context=None):
+
+        imd_obj = self.pool.get('ir.model.data')
+        model, group1_id = imd_obj.get_object_reference(
+            cr, uid, 'base', 'group_hr_manager')
+        model, group2_id = imd_obj.get_object_reference(
+            cr, uid, 'hr_security', 'group_hr_director')
+        data = self.pool.get('res.groups').read(
+            cr, uid, [group1_id, group2_id], ['users'], context=context)
+        user_ids = list(set(data[0]['users'] + data[1]['users']))
+        self.message_subscribe_users(
+            cr, uid, ids, user_ids=user_ids, context=context)
+
+        return self._state(cr, uid, ids, state, context=context)
+
+    def state_confirm(self, cr, uid, ids, context=None):
+
+        return self._state_subscribe_users(cr, uid, ids, 'confirm', context=context)
+
+    def state_exception(self, cr, uid, ids, context=None):
+
+        return self._state_subscribe_users(cr, uid, ids, 'exception', context=context)
+
+    def state_recruitment(self, cr, uid, ids, context=None):
+
+        return self._state_subscribe_users(cr, uid, ids, 'recruitment', context=context)
+
+    def state_done(self, cr, uid, ids, context=None):
+
+        return self._state(cr, uid, ids, 'done', context=context)
+
+    def state_cancel(self, cr, uid, ids, context=None):
+
+        return self._state(cr, uid, ids, 'cancel', context=context)

=== added file 'hr_labour_recruitment/hr_recruitment_data.xml'
--- hr_labour_recruitment/hr_recruitment_data.xml	1970-01-01 00:00:00 +0000
+++ hr_labour_recruitment/hr_recruitment_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+                        
+        <!-- Alert-related subtypes for messaging / Chatter -->
+        
+        <record id="mt_alert_request_confirmed" model="mail.message.subtype">
+            <field name="name">Personnel Request Confirmed</field>
+            <field name="res_model">hr.recruitment.request</field>
+            <field name="description">Personnel Request is awaiting approval</field>
+        </record>
+        
+        <record id="mt_alert_request_exception" model="mail.message.subtype">
+            <field name="name">Personnel Request in Exception</field>
+            <field name="res_model">hr.recruitment.request</field>
+            <field name="description">Personnel Request exceeds maximum number of employees for the job. Awaiting approval.</field>
+        </record>
+        
+        <record id="mt_alert_request_approved" model="mail.message.subtype">
+            <field name="name">Personnel Request Approved</field>
+            <field name="res_model">hr.recruitment.request</field>
+            <field name="description">Personnel Request has been approved</field>
+        </record>
+        
+        <record id="mt_alert_request_declined" model="mail.message.subtype">
+            <field name="name">Personnel Request Declined</field>
+            <field name="res_model">hr.contract</field>
+            <field name="description">Personnel Request has been declined</field>
+        </record>
+
+    </data>
+</openerp>

=== added file 'hr_labour_recruitment/hr_recruitment_view.xml'
--- hr_labour_recruitment/hr_recruitment_view.xml	1970-01-01 00:00:00 +0000
+++ hr_labour_recruitment/hr_recruitment_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <!-- Menus -->
+        
+        <menuitem name="Recruitment"
+            id="submenu_hr_recruitment"
+            parent="hr.menu_hr_main"
+            sequence="110" groups="base.group_hr_user"/>
+        
+        <!-- Delete Recruitment top-level menu and put it under our own submenu -->
+        <delete model="ir.ui.menu" id="base.menu_crm_case_job_req_main"/>
+        <delete model="ir.ui.menu" id="hr_recruitment.menu_crm_case_categ0_act_job"/>
+        <menuitem
+            name="Applications"
+            parent="submenu_hr_recruitment"
+            id="menu_crm_case_categ0_act_job" action="hr_recruitment.crm_case_categ0_act_job" sequence="1"/>
+        
+        <!-- Recruitment Requests -->
+
+        <record id="view_hr_recruitment_request_filter" model="ir.ui.view">
+            <field name="name">hr.recruitment.request.filter</field>
+            <field name="model">hr.recruitment.request</field>
+            <field name="arch" type="xml">
+                <search string="Search Personnel Requests">
+                    <filter name="draft" string="In Draft" domain="[('state','=','draft')]" help="Unconfirmed Requests"/>
+                    <filter name="to_approve" string="To Approve" domain="[('state','in',['confirm','exception'])]" help="Confirmed Requests"/>
+                    <filter name="to_complete" string="To Complete" domain="[('state','=','recruitment')]" help="Requests in Recruitment"/>
+                    <field name="job_id"/>
+                    <field name="department_id"/>
+                    <group expand="0" string="Group By...">
+                        <filter string="Job" icon="terp-personal" domain="[]" context="{'group_by':'job_id'}"/>
+                        <filter string="Department" icon="terp-personal+" domain="[]" context="{'group_by':'department_id'}"/>
+                    </group>
+                </search>
+            </field>
+        </record>
+        
+        <record id="view_recruitment_request_tree" model="ir.ui.view">
+            <field name="name">hr.recruitment.request.tree</field>
+            <field name="model">hr.recruitment.request</field>
+            <field name="arch" type="xml">
+                <tree string="Personnel Requests">
+                    <field name="job_id"/>
+                    <field name="department_id"/>
+                    <field name="user_id"/>
+                    <field name="number"/>
+                    <field name="state"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_recruitment_request_form" model="ir.ui.view">
+            <field name="name">hr.recruitment.request.form</field>
+            <field name="model">hr.recruitment.request</field>
+            <field name="arch" type="xml">
+                <form string="Personnel Request" version="7.0">
+                    <header>
+                        <button name="signal_confirm" type="workflow" states="draft" groups="base.group_hr_user" string="Confirm" class="oe_highlight"/>
+                        <button name="signal_approve" type="workflow" states="confirm,exception"  groups="hr_security.group_hr_director" string="Approve" class="oe_highlight"/>
+                        <button name="signal_decline" type="workflow" states="confirm,exception"  groups="hr_security.group_hr_director" string="Decline" class="oe_highlight"/>
+                        <button name="signal_done" type="workflow" states="recruitment"  groups="base.group_hr_manager" string="Done" class="oe_highlight"/>
+                        <button name="signal_cancel" type="workflow" states="confirm,exception,recruitment"  groups="base.group_hr_user" string="Cancel" class="oe_highlight"/>
+                        <field name="state" widget="statusbar" statusbar_visible="draft,confirm,exception,recruitment" statusbar_colors='{"exception":"read"}'/>
+                    </header>
+                    <group>
+                        <group>
+                            <field name="user_id"/>
+                            <field name="job_id" on_change="onchange_job(job_id)"/>
+                            <field name="number"/>
+                        </group>
+                        <group>
+                            <field name="name"/>
+                            <field name="department_id"/>
+                        </group>
+                        <group>
+                            <field name="current_number"/>
+                            <field name="max_number"/>
+                        </group>
+                    </group>
+                    <separator string="Reason for Request"/>
+                    <field name="reason" nolabel="1"/>
+                    <newline/>
+                    <div class="oe_chatter">
+                        <field name="message_follower_ids" widget="mail_followers"/>
+                        <field name="message_ids" widget="mail_thread"/>
+                    </div>
+                </form>
+            </field>
+        </record>
+        <record id="open_recruitment_request" model="ir.actions.act_window">
+            <field name="name">Personnel Requests</field>
+            <field name="res_model">hr.recruitment.request</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="view_id" eval="False"/>
+            <field name="context">{'search_default_to_approve':1, 'search_default_to_complete':1}</field>
+            <field name="search_view_id" ref="view_hr_recruitment_request_filter"/>
+            <field name="help" type="html">
+              <p class="oe_view_nocontent_create">
+                New Personnel Requests to approve or to complete.
+              </p><p>
+                You should fill out a Personnel Request form to recruit new employees. Once
+                you have confirmed it the HR Director should approve it.
+              </p>
+            </field>
+        </record>
+        <menuitem id="menu_recruitment_request"
+            action="open_recruitment_request"
+            parent="submenu_hr_recruitment"
+            groups="base.group_hr_user"
+            sequence="15"/>
+        
+        <!-- Jobs -->
+        <record id="view_job_form" model="ir.ui.view">
+            <field name="name">hr.job.form.inherit</field>
+            <field name="model">hr.job</field>
+            <field name="inherit_id" ref="hr.view_hr_job_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//button[@name='job_recruitement']" position="replace"/>
+                    <xpath expr="//button[@name='job_open']" position="replace"/>
+                    <xpath expr="//field[@name='expected_employees']" position="after">
+                        <field name="max_employees"/>
+                        <field name="max_employees_fuzz"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+        <record id="view_job_tree" model="ir.ui.view">
+            <field name="name">hr.job.tree.inherit</field>
+            <field name="model">hr.job</field>
+            <field name="inherit_id" ref="hr.view_hr_job_tree"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='expected_employees']" position="before">
+                    <field name="max_employees"/>
+                    <field name="max_employees_fuzz"/>
+                </xpath>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_labour_recruitment/hr_recruitment_workflow.xml'
--- hr_labour_recruitment/hr_recruitment_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_labour_recruitment/hr_recruitment_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <!-- Employee Workflow Definition -->
+        <record id="wkf_recruitment_request" model="workflow">
+            <field name="name">hr.recruitment.request.basic</field>
+            <field name="osv">hr.recruitment.request</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (Stages) -->
+        
+        <record id="act_draft" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_recruitment_request"/>
+            <field name="name">draft</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'draft'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_confirm" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_recruitment_request"/>
+            <field name="name">confirm</field>
+            <field name="kind">function</field>
+            <field name="action">state_confirm()</field>
+        </record>
+        
+        <record id="act_exception" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_recruitment_request"/>
+            <field name="name">exception</field>
+            <field name="kind">function</field>
+            <field name="action">state_exception()</field>
+        </record>
+        
+        <record id="act_recruitment" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_recruitment_request"/>
+            <field name="name">recruitment</field>
+            <field name="kind">function</field>
+            <field name="action">state_recruitment()</field>
+        </record>
+        
+        <record id="act_decline" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_recruitment_request"/>
+            <field name="name">decline</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'decline'})</field>
+        </record>
+        
+        <record id="act_done" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_recruitment_request"/>
+            <field name="name">done</field>
+            <field name="kind">function</field>
+            <field name="action">state_done()</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        <record id="act_cancel" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_recruitment_request"/>
+            <field name="name">cancel</field>
+            <field name="kind">function</field>
+            <field name="action">state_cancel()</field>
+            <field name="flow_stop">True</field>
+        </record>
+        
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="draft2confirm" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_confirm"/>
+            <field name="condition">condition_exception() == False</field>
+            <field name="signal">signal_confirm</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="draft2exception" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_exception"/>
+            <field name="condition">condition_exception() == True</field>
+            <field name="signal">signal_confirm</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="confirm2recruitment" model="workflow.transition">
+            <field name="act_from" ref="act_confirm"/>
+            <field name="act_to" ref="act_recruitment"/>
+            <field name="signal">signal_approve</field>
+            <field name="group_id" ref="hr_security.group_hr_director"/>
+        </record>
+        
+        <record id="exception2recruitment" model="workflow.transition">
+            <field name="act_from" ref="act_exception"/>
+            <field name="act_to" ref="act_recruitment"/>
+            <field name="signal">signal_approve</field>
+            <field name="group_id" ref="hr_security.group_hr_director"/>
+        </record>
+        
+        <record id="confirm2decline" model="workflow.transition">
+            <field name="act_from" ref="act_confirm"/>
+            <field name="act_to" ref="act_decline"/>
+            <field name="signal">signal_decline</field>
+            <field name="group_id" ref="hr_security.group_hr_director"/>
+        </record>
+        
+        <record id="recruitment2done" model="workflow.transition">
+            <field name="act_from" ref="act_recruitment"/>
+            <field name="act_to" ref="act_done"/>
+            <field name="signal">signal_done</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="confirm2cancel" model="workflow.transition">
+            <field name="act_from" ref="act_confirm"/>
+            <field name="act_to" ref="act_cancel"/>
+            <field name="signal">signal_cancel</field>
+            <field name="group_id" ref="base.group_hr_user"/>
+        </record>
+        
+        <record id="recruitment2cancel" model="workflow.transition">
+            <field name="act_from" ref="act_recruitment"/>
+            <field name="act_to" ref="act_cancel"/>
+            <field name="signal">signal_cancel</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+    
+    </data>
+</openerp>

=== added directory 'hr_labour_recruitment/security'
=== added file 'hr_labour_recruitment/security/ir.model.access.csv'
--- hr_labour_recruitment/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_labour_recruitment/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,4 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_recruitment_request_user,access_hr_recruitment_request,model_hr_recruitment_request,base.group_hr_user,1,1,1,1
+access_hr_recruitment_request_manager,access_hr_recruitment_request,model_hr_recruitment_request,base.group_hr_manager,1,1,1,1
+access_hr_recruitment_request_director,access_hr_recruitment_request,model_hr_recruitment_request,hr_security.group_hr_director,1,1,0,0

=== added directory 'hr_labour_union'
=== added file 'hr_labour_union/__init__.py'
--- hr_labour_union/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_labour_union/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr

=== added file 'hr_labour_union/__openerp__.py'
--- hr_labour_union/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_labour_union/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,52 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Labour Union',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Labour Union
+=====================
+
+This module adds:
+    * Field on employee record denoting whether the employee is a member of a labour union
+    * Salary rule for deducting union fee from union members
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr',
+        'hr_payroll',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'hr_labour_union_data.xml',
+        'hr_labour_union_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added file 'hr_labour_union/hr.py'
--- hr_labour_union/hr.py	1970-01-01 00:00:00 +0000
+++ hr_labour_union/hr.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,44 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp.osv import fields, osv
+
+
+class hr_employee(osv.Model):
+
+    _name = 'hr.employee'
+    _inherit = 'hr.employee'
+
+    _columns = {
+        'is_labour_union': fields.boolean('Labour Union Member'),
+        'labour_union_date': fields.date('Date of Membership'),
+    }
+
+
+class hr_contract(osv.Model):
+
+    _name = 'hr.contract'
+    _inherit = 'hr.contract'
+
+    _columns = {
+        'is_labour_union': fields.related('employee_id', 'is_labour_union', type='boolean',
+                                          store=True, string='Labour Union Member'),
+    }

=== added file 'hr_labour_union/hr_labour_union_data.xml'
--- hr_labour_union/hr_labour_union_data.xml	1970-01-01 00:00:00 +0000
+++ hr_labour_union/hr_labour_union_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <!-- Payroll Register -->
+        <record id="hr_register_labour_union" model="hr.contribution.register">
+            <field name="name">Register for Labour Union Dues</field>
+        </record>
+        
+        <record id="hr_payroll_rule_labour_union" model="hr.salary.rule">
+            <field name="code">LU</field>
+            <field name="name">Labour Union Contribution</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = contract.is_labour_union and categories.GROSS >= 0.01</field>
+            <field name="amount_select">fix</field>
+            <field eval="0" name="amount_fix"/>
+            <field name="register_id" ref="hr_register_labour_union"/>
+            <field name="sequence" eval="2200"/>
+            <field name="note"></field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_labour_union/hr_labour_union_view.xml'
--- hr_labour_union/hr_labour_union_view.xml	1970-01-01 00:00:00 +0000
+++ hr_labour_union/hr_labour_union_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<openerp>
+    <data>
+        
+        <record id="view_hr_employee_form" model="ir.ui.view">
+            <field name="name">hr.employee.form.labourunion</field>
+            <field name="model">hr.employee</field>
+            <field name="inherit_id" ref="hr_contract.hr_hr_employee_view_form2"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='manager']" position="after">
+                    <field name="is_labour_union"/>
+                    <field name="labour_union_date"/>
+                </xpath>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_payroll_extension'
=== added file 'hr_payroll_extension/__init__.py'
--- hr_payroll_extension/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_extension/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_payroll

=== added file 'hr_payroll_extension/__openerp__.py'
--- hr_payroll_extension/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_extension/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,61 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Payroll Extension',
+    'category': 'Human Resources',
+    'author': 'Michael Telahun Makonnen',
+    'website': 'http://miketelahun.wordpress.com',
+    'version': '1.0',
+    'description': """
+Extended set of Payroll Rules and Structures
+============================================
+
+    - Detailed caclculatation of worked hours, leaves, overtime, etc
+    - Overtime
+    - Paid and Unpaid Leaves
+    - Federal Income Tax Withholding rules
+    - Provident/Pension Fund contributions
+    - Various Earnings and Deductions
+    - Payroll Report
+    """,
+    'depends': [
+        'hr_attendance',
+        'hr_payroll',
+        'hr_payroll_period',
+        'hr_policy_absence',
+        'hr_policy_ot',
+        'hr_policy_presence',
+        'hr_public_holidays',
+        'hr_schedule',
+    ],
+    'init_xml': [],
+    'update_xml': [
+        'data/hr_payroll_extension_data.xml',
+        'data/hr.salary.rule.csv',
+        'hr_payroll_view.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [],
+    'active': False,
+    'installable': True,
+}

=== added directory 'hr_payroll_extension/data'
=== added file 'hr_payroll_extension/data/hr.salary.rule.csv'
--- hr_payroll_extension/data/hr.salary.rule.csv	1970-01-01 00:00:00 +0000
+++ hr_payroll_extension/data/hr.salary.rule.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,13 @@
+id,code,name,condition_select,condition_range,condition_range_min,condition_range_max,amount_select,amount_percentage,amount_fix,amount_percentage_base,parent_rule_id/id,category_id/id,sequence
+fitw_10,2003FIT00,Exempt,range,TXBLINCOM,0,150,fix,0,0,,hr_payroll_rule_fit,hr_categ_fitcalc,1000
+fitw_11,2003FIT10,10.00%,range,TXBLINCOM,151,650,percentage,10,0,TXBLINCOM - 150,hr_payroll_rule_fit,hr_categ_fitcalc,1010
+fitw_12,2003FIT15A,15.00%,range,TXBLINCOM,650,1400,percentage,15,0,TXBLINCOM - 650,hr_payroll_rule_fit,hr_categ_fitcalc,1015
+fitw_13,2003FIT15B,15%-Fixed,range,TXBLINCOM,650,1400,fix,0,50,,hr_payroll_rule_fit,hr_categ_fitcalc,1016
+fitw_14,2003FIT20A,20.00%,range,TXBLINCOM,1400,2350,percentage,20,0,TXBLINCOM - 1400,hr_payroll_rule_fit,hr_categ_fitcalc,1020
+fitw_15,2003FIT20B,20%-Fixed,range,TXBLINCOM,1400,2350,fix,0,162.5,,hr_payroll_rule_fit,hr_categ_fitcalc,1021
+fitw_16,2003FIT25A,25.00%,range,TXBLINCOM,2350,3550,percentage,25,0,TXBLINCOM - 2350,hr_payroll_rule_fit,hr_categ_fitcalc,1025
+fitw_17,2003FIT25B,25%-Fixed,range,TXBLINCOM,2350,3550,fix,0,352.5,,hr_payroll_rule_fit,hr_categ_fitcalc,1026
+fitw_18,2003FIT30A,30.00%,range,TXBLINCOM,3550,5000,percentage,30,0,TXBLINCOM - 3550,hr_payroll_rule_fit,hr_categ_fitcalc,1030
+fitw_19,2003FIT30B,30%-Fixed,range,TXBLINCOM,3550,5000,fix,0,652.5,,hr_payroll_rule_fit,hr_categ_fitcalc,1031
+fitw_20,2003FIT35A,35.00%,range,TXBLINCOM,5000,1000000000,percentage,35,0,TXBLINCOM - 5000,hr_payroll_rule_fit,hr_categ_fitcalc,1035
+fitw_21,2003FIT35B,35%-Fixed,range,TXBLINCOM,5000,1000000000,fix,0,1087.5,,hr_payroll_rule_fit,hr_categ_fitcalc,1036

=== added file 'hr_payroll_extension/data/hr_payroll_extension_data.xml'
--- hr_payroll_extension/data/hr_payroll_extension_data.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_extension/data/hr_payroll_extension_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        
+        <!-- Delete some default leave types from the database -->
+        <delete model="hr.holidays.status" search="[('name','like','Legal Leaves%')]"/>
+        <delete model="hr.holidays.status" search="[('name','=','Compensatory Days')]"/>
+        <delete model="hr.holidays.status" search="[('name','=','Unpaid')]"/>
+
+        <!-- Contribution Register -->
+        
+        <record id="hr_register_ot" model="hr.contribution.register">
+            <field name="name">Register for Overtime</field>
+        </record>
+
+        <record id="hr_register_transport_allowance" model="hr.contribution.register">
+            <field name="name">Register for Transport Allowance</field>
+        </record>
+
+        <record id="hr_register_bonus" model="hr.contribution.register">
+            <field name="name">Register for Bonus</field>
+        </record>
+
+        <record id="hr_register_ule" model="hr.contribution.register">
+            <field name="name">Register for Unused Leaves converted into cash</field>
+        </record>
+
+        <record id="hr_register_provident_fund" model="hr.contribution.register">
+            <field name="name">Register for Provident Fund</field>
+        </record>
+
+        <record id="hr_register_pension_fund" model="hr.contribution.register">
+            <field name="name">Register for Pension Fund</field>
+        </record>
+
+        <record id="hr_register_fit" model="hr.contribution.register">
+            <field name="name">Register for Federal Income Tax</field>
+        </record>
+
+        <record id="hr_company_transport_register" model="hr.contribution.register">
+            <field name="name">Register for Company Provided Transport Deduction</field>
+        </record>
+
+        <record id="hr_register_penalty" model="hr.contribution.register">
+            <field name="name">Register for Penalties</field>
+        </record>
+
+        <record id="hr_register_disciplinary" model="hr.contribution.register">
+            <field name="name">Register for Disciplinary Penalties</field>
+        </record>
+
+        <record id="hr_register_idcard" model="hr.contribution.register">
+            <field name="name">Register for ID Card Replacement</field>
+        </record>
+
+        <record id="hr_register_advance" model="hr.contribution.register">
+            <field name="name">Register for Repayment of Salary Advances</field>
+        </record>
+
+        <record id="hr_register_other_deductions" model="hr.contribution.register">
+            <field name="name">Register for Other Deductions</field>
+        </record>
+
+        <!--
+             DO NOT CHANGE THE CODES BELOW!!!
+             The codes are used in the payroll register module to print out
+             the payroll register.
+        -->
+
+        <!-- Salary Rule Categories -->
+        
+        <record id="hr_categ_ot" model="hr.salary.rule.category">
+            <field name="name">Overtime</field>
+            <field name="code">OT</field>
+        </record>
+
+        <record id="hr_categ_txbl" model="hr.salary.rule.category">
+            <field name="name">Taxable Earnings</field>
+            <field name="code">TXBL</field>
+        </record>
+
+        <record id="hr_categ_txexmpt" model="hr.salary.rule.category">
+            <field name="name">Tax Exempt Earnings</field>
+            <field name="code">TXEXMPT</field>
+        </record>
+
+        <record id="hr_categ_fitcalc" model="hr.salary.rule.category">
+            <field name="code">FITCALC</field>
+            <field name="name">Federal Income Tax Withholding</field>
+        </record>
+
+        <record id="hr_categ_dedtotal" model="hr.salary.rule.category">
+            <field name="code">DEDTOTAL</field>
+            <field name="name">Deductions Total</field>
+        </record>
+        
+        <record id="hr_categ_er" model="hr.salary.rule.category">
+            <field name="code">ER</field>
+            <field name="name">Employer Contribution</field>
+        </record>
+        
+        <!-- Basic Income based on attendance -->
+        <record id="hr_salary_rule_basic_attendance" model="hr.salary.rule">
+            <field name="code">WORKWAGE</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = contract.wage_hourly</field>
+            <field name="quantity">((utils.PPF.amount &lt; 0.99) and worked_days.MAX.number_of_hours or  208) - worked_days.AWOL.number_of_hours</field>
+            <field name="category_id" ref="hr_payroll.BASIC"/>
+            <field name="name">Basic Salary based on Attendance</field>
+            <field name="sequence" eval="2"/>
+            <field name="note">
+                Calculate monthly basic wage according to attendance records of employee. Does not include Overtime.
+            </field>
+        </record>
+        
+        <!-- Basic Income reduced based on no. of hours absent -->
+        <record id="hr_salary_rule_adjust_attendance" model="hr.salary.rule">
+            <field name="code">ATTADJUST</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = worked_days.AWOL.number_of_hours == worked_days.MAX.number_of_hours and -(categories.BASIC / worked_days.AWOL.number_of_hours) or -(contract.wage_hourly)</field>
+            <field name="quantity">min(worked_days.AWOL.number_of_hours, 208)</field>
+            <field name="category_id" ref="hr_payroll.BASIC"/>
+            <field name="name">Salary Adjustment Based on Attendance</field>
+            <field name="sequence" eval="3"/>
+            <field name="note">
+                Adjust basic salary by number of hours absent without leave.
+            </field>
+        </record>
+
+        <!-- Overtime rules -->
+        
+        <record id="hr_salary_rule_daily_ot" model="hr.salary.rule">
+            <field name="code">OTD</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = worked_days.WORKOTD.number_of_hours > 0</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = worked_days.WORKOTD.rate * contract.wage_hourly</field>
+            <field name="quantity">worked_days.WORKOTD.number_of_hours</field>
+            <field name="category_id" ref="hr_categ_ot"/>
+            <field name="name">Day OverTime</field>
+            <field name="register_id" ref="hr_register_ot"/>
+            <field name="sequence" eval="30"/>
+            <field name="note">Applicable to both salaried and hourly employees.</field>
+        </record>
+
+        <record id="hr_salary_rule_night_ot" model="hr.salary.rule">
+            <field name="code">OTN</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = worked_days.WORKOTN.number_of_hours > 0</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = worked_days.WORKOTN.rate * contract.wage_hourly</field>
+            <field name="quantity">worked_days.WORKOTN.number_of_hours</field>
+            <field name="category_id" ref="hr_categ_ot"/>
+            <field name="name">Night OverTime</field>
+            <field name="register_id" ref="hr_register_ot"/>
+            <field name="sequence" eval="33"/>
+            <field name="note">Applicable to both salaried and hourly employees.</field>
+        </record>
+
+        <record id="hr_salary_rule_weekly_ot" model="hr.salary.rule">
+            <field name="code">OTW</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = worked_days.WORKOTW.number_of_hours > 0</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = worked_days.WORKOTW.rate * contract.wage_hourly</field>
+            <field name="quantity">worked_days.WORKOTW.number_of_hours</field>
+            <field name="category_id" ref="hr_categ_ot"/>
+            <field name="name">Weekly OverTime</field>
+            <field name="register_id" ref="hr_register_ot"/>
+            <field name="sequence" eval="35"/>
+            <field name="note">Applicable to both salaried and hourly employees.</field>
+        </record>
+
+        <record id="hr_salary_rule_restday_ot" model="hr.salary.rule">
+            <field name="code">OTR</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = worked_days.WORKOTR.number_of_hours > 0</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = worked_days.WORKOTR.rate * contract.wage_hourly</field>
+            <field name="quantity">worked_days.WORKOTR.number_of_hours</field>
+            <field name="category_id" ref="hr_categ_ot"/>
+            <field name="name">Rest Day OverTime</field>
+            <field name="register_id" ref="hr_register_ot"/>
+            <field name="sequence" eval="36"/>
+            <field name="note">Applicable to both salaried and hourly employees.</field>
+        </record>
+
+        <record id="hr_salary_rule_holiday_ot" model="hr.salary.rule">
+            <field name="code">OTH</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = worked_days.WORKOTH.number_of_hours > 0</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = worked_days.WORKOTH.rate * contract.wage_hourly</field>
+            <field name="quantity">worked_days.WORKOTH.number_of_hours</field>
+            <field name="category_id" ref="hr_categ_ot"/>
+            <field name="name">Holiday OverTime</field>
+            <field name="register_id" ref="hr_register_ot"/>
+            <field name="sequence" eval="40"/>
+            <field name="note">Not applicable to salaried employees.</field>
+        </record>
+
+        <!-- Hr Salary Rules for allowance -->
+        
+        <record id="hr_salary_rule_houserentallowance" model="hr.salary.rule">
+            <field name="code">HRA</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = worked_days.WORK100.number_of_hours and contract.hra_amount or 0</field>
+            <field name="category_id" ref="hr_payroll.ALW"/>
+            <field name="name">House Rent Allowance</field>
+            <field name="sequence" eval="50"/>
+            <field name="note">
+                An allowance given to the employee for taking care of living accommodation expenses.
+            </field>
+        </record>
+
+        <record id="hr_salary_trans_allowance" model="hr.salary.rule">
+            <field name="code">TRA</field>
+            <field name="name">Transport Allowance</field>
+            <field name="category_id" ref="hr_payroll.ALW"/>
+            <field name="amount_select">fix</field>
+            <field eval="0" name="amount_fix"/>
+            <field name="register_id" ref="hr_register_transport_allowance"/>
+            <field name="sequence" eval="100"/>
+            <field name="note"></field>
+        </record>
+
+        <!-- If you change python condition here don't forget to also change
+             corresponding tax exemption rule -->
+         <record id="hr_salary_rule_trans_variable_allowance" model="hr.salary.rule">
+            <field name="code">TRVA</field>
+            <field name="name">Transport Allowance</field>
+            <field name="category_id" ref="hr_payroll.ALW"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = contract.wage &lt; 5000</field>
+            <field name="amount_select">fix</field>
+            <field eval="0" name="amount_fix"/>
+            <field name="quantity">worked_days.WORK100.number_of_days + worked_days.WORKOTR.number_of_days + worked_days.WORKOTH.number_of_days</field>
+            <field name="register_id" ref="hr_register_transport_allowance"/>
+            <field name="sequence" eval="150"/>
+         </record>
+
+        <record id="hr_salary_rule_bonus" model="hr.salary.rule">
+            <field name="code">BONUS</field>
+            <field name="name">Bonus</field>
+            <field name="category_id" ref="hr_payroll.ALW"/>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = inputs.BNS.amount</field>
+            <field name="register_id" ref="hr_register_bonus"/>
+            <field name="sequence" eval="250"/>
+            <field name="note"></field>
+        </record>
+
+        <record id="hr_salary_rule_arrears" model="hr.salary.rule">
+            <field name="code">ARRE</field>
+            <field name="name">Arrears</field>
+            <field name="category_id" ref="hr_payroll.ALW"/>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = inputs.ARS.amount</field>
+            <field eval="0.0" name="amount_fix"/>
+            <field name="sequence" eval="300"/>
+            <field name="note"></field>
+        </record>
+
+        <record id="hr_salary_rule_ule" model="hr.salary.rule">
+            <field name="code">ULE</field>
+            <field name="name">Unused Leave</field>
+            <field name="category_id" ref="hr_payroll.ALW"/>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = inputs.ULE.amount</field>
+            <field name="register_id" ref="hr_register_ule"/>
+            <field name="sequence" eval="350"/>
+            <field name="note"></field>
+        </record>
+
+        <record id="hr_trans_allowance_exempt" model="hr.salary.rule">
+            <field name="code">TRAEX</field>
+            <field name="name">Transport Allowance - Tax Exempt</field>
+            <field name="category_id" ref="hr_categ_txexmpt"/>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = min(rules.TRA.amount_fix,BASIC*0.15)</field>
+            <field name="sequence" eval="700"/>
+            <field name="note">
+                Tax-Exempt portion of transport allowance.
+            </field>
+        </record>
+
+        <!-- If you change python condition here don't forget to also change
+             corresponding allowance -->
+        <record id="hr_trans_variable_allowance_exempt" model="hr.salary.rule">
+            <field name="code">TRVAEX</field>
+            <field name="name">Transport Allowance - Tax Exempt</field>
+            <field name="category_id" ref="hr_categ_txexmpt"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = contract.wage &lt; 5000</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = min(rules.TRVA.amount_fix * (worked_days.WORK100.number_of_days + worked_days.WORKOTR.number_of_days + worked_days.WORKOTH.number_of_days), categories.BASIC*0.15)</field>
+            <field name="sequence" eval="750"/>
+            <field name="note">
+                Tax-Exempt portion of variable transport allowance.
+            </field>
+        </record>
+        
+        <record id="hr_payroll_rule_taxable_income" model="hr.salary.rule">
+            <field name="code">TXBLINCOM</field>
+            <field name="name">Taxable Earnings</field>
+            <field name="category_id" ref="hr_categ_txbl"/>
+            <field name="condition_select">none</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = categories.BASIC + categories.OT + categories.ALW - categories.TXEXMPT</field>
+            <field name="sequence" eval="980"/>
+        </record>
+
+        <record id="hr_payroll.hr_rule_taxable" model="hr.salary.rule">
+            <field name="sequence" eval="990"/>
+            <field name="amount_python_compute">result = categories.BASIC + categories.OT + categories.ALW</field>
+        </record>
+
+        <record id="hr_payroll.hr_rule_net" model="hr.salary.rule">
+            <field name="amount_python_compute">result = categories.BASIC + categories.OT + categories.ALW - categories.FITCALC - categories.DED</field>
+            <field name="sequence" eval="4000"/>
+        </record>
+        
+        <!-- hr salary rules for Deductions -->
+
+        <record id="hr_payroll_rule_fit" model="hr.salary.rule">
+            <field name="code">FIT</field>
+            <field name="name">Federal Income Tax Withholding</field>
+            <field name="category_id" ref="hr_categ_fitcalc"/>
+            <field name="condition_select">none</field>
+            <field name="appears_on_payslip" eval="False"/>
+            <field name="register_id" ref="hr_register_fit"/>
+            <field name="sequence" eval="1000"/>
+            <field name="note"></field>
+        </record>
+
+        <record id="hr_payroll_rule_provfee" model="hr.salary.rule">
+            <field name="code">PROVFEE</field>
+            <field name="name">EE Provident Fund Contribution</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = categories.BASIC * 0.06</field>
+            <field name="register_id" ref="hr_register_provident_fund"/>
+            <field name="sequence" eval="2000"/>
+            <field name= "note">Employee contribution to Provident Fund.</field>
+        </record>
+
+        <record id="hr_payroll_rule_penfee" model="hr.salary.rule">
+            <field name="code">PENFEE</field>
+            <field name="name">EE Pension Fund Contribution</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = categories.BASIC * 0.06</field>
+            <field name="register_id" ref="hr_register_pension_fund"/>
+            <field name="sequence" eval="2100"/>
+            <field name= "note">Employee contribution to Pension Fund.</field>
+        </record>
+
+        <record id="hr_payroll_rule_cpt" model="hr.salary.rule">
+            <field name="code">CPT</field>
+            <field name="name">Deduction for Company Provided Transport</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">fix</field>
+            <field eval="0" name="amount_fix"/>
+            <field name="register_id" ref="hr_company_transport_register"/>
+            <field name="sequence" eval="2300"/>
+            <field name="appears_on_payslip" eval="False"/>
+            <field name="note">
+                Deductions for company provided transport such as bus service for staff.
+            </field>
+        </record>
+
+        <record id="hr_payroll_rule_idcard" model="hr.salary.rule">
+            <field name="code">IDCARD</field>
+            <field name="name">ID Card Replacement</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = inputs.IDCARD.amount</field>
+            <field name="register_id" ref="hr_register_idcard"/>
+            <field name="sequence" eval="2320"/>
+        </record>
+
+        <record id="hr_payroll_rule_advance" model="hr.salary.rule">
+            <field name="code">ADV</field>
+            <field name="name">Salary Advance Repayment</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = inputs.ADVANCE.amount</field>
+            <field name="register_id" ref="hr_register_advance"/>
+            <field name="sequence" eval="2330"/>
+        </record>
+
+        <record id="hr_payroll_rule_penalty" model="hr.salary.rule">
+            <field name="code">PD</field>
+            <field name="name">Penalty</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = inputs.PENALTY.amount</field>
+            <field name="register_id" ref="hr_register_penalty"/>
+            <field name="sequence" eval="2400"/>
+            <field name="note">Non-disciplinary penalties such as library late fees, etc...</field>
+        </record>
+
+        <record id="hr_payroll_rule_dad" model="hr.salary.rule">
+            <field name="code">DAD</field>
+            <field name="name">Disciplinary Action</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = inputs.DISCA.amount</field>
+            <field name="register_id" ref="hr_register_disciplinary"/>
+            <field name="sequence" eval="2500"/>
+            <field name="note">Deductions as a result of disciplinary action.</field>
+        </record>
+
+        <record id="hr_payroll_rule_od" model="hr.salary.rule">
+            <field name="code">OD</field>
+            <field name="name">Other Deductions</field>
+            <field name="category_id" ref="hr_payroll.DED"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">fix</field>
+            <field eval="0" name="amount_fix"/>
+            <field name="register_id" ref="hr_register_other_deductions"/>
+            <field name="sequence" eval="2600"/>
+            <field name="note"></field>
+        </record>
+
+        <record id="hr_payroll_rule_dedtotal" model="hr.salary.rule">
+            <field name="code">DEDTOTAL</field>
+            <field name="name">Deductions Total</field>
+            <field name="category_id" ref="hr_categ_dedtotal"/>
+            <field name="condition_select">none</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = categories.FITCALC + categories.DED</field>
+            <field name="sequence" eval="2990"/>
+        </record>
+
+        <record id="hr_payroll_rule_provfer" model="hr.salary.rule">
+            <field name="code">PROVFER</field>
+            <field name="name">ER Provident Fund Contribution</field>
+            <field name="category_id" ref="hr_categ_er"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = categories.BASIC * 0.08</field>
+            <field name="appears_on_payslip" eval="True"/>
+            <field name="register_id" ref="hr_register_provident_fund"/>
+            <field name="sequence" eval="3000"/>
+            <field name="note">Employer contribution to provident fund.</field>
+        </record>
+
+        <record id="hr_payroll_rule_penfer" model="hr.salary.rule">
+            <field name="code">PENFER</field>
+            <field name="name">ER Pension Fund Contribution</field>
+            <field name="category_id" ref="hr_categ_er"/>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.GROSS >= 0.01</field>
+            <field name="amount_select">code</field>
+            <field name="amount_python_compute">result = categories.BASIC * 0.08</field>
+            <field name="appears_on_payslip" eval="True"/>
+            <field name="register_id" ref="hr_register_pension_fund"/>
+            <field name="sequence" eval="3100"/>
+            <field name="note">Employer contribution to pension fund.</field>
+        </record>
+
+        <!-- Rule Inputs -->
+
+        <record id="hr_rule_input_bonus" model="hr.rule.input">
+            <field name="code">BNS</field>
+            <field name="name">Employee Bonus</field>
+            <field name="input_id" ref="hr_salary_rule_bonus"/>
+        </record>
+
+        <record id="hr_rule_input_arrears" model="hr.rule.input">
+            <field name="code">ARS</field>
+            <field name="name">Arrears</field>
+            <field name="input_id" ref="hr_salary_rule_arrears"/>
+        </record>
+
+        <record id="hr_rule_input_ule" model="hr.rule.input">
+            <field name="code">ULE</field>
+            <field name="name">Unused Leave</field>
+            <field name="input_id" ref="hr_salary_rule_ule"/>
+        </record>
+
+        <record id="hr_rule_input_idcard" model="hr.rule.input">
+            <field name="code">IDCARD</field>
+            <field name="name">ID Card Replacement Fee</field>
+            <field name="input_id" ref="hr_payroll_rule_idcard"/>
+        </record>
+
+        <record id="hr_rule_input_advance" model="hr.rule.input">
+            <field name="code">ADVANCE</field>
+            <field name="name">Salary Advance</field>
+            <field name="input_id" ref="hr_payroll_rule_advance"/>
+        </record>
+
+        <record id="hr_rule_input_penalty" model="hr.rule.input">
+            <field name="code">PENALTY</field>
+            <field name="name">Penalty</field>
+            <field name="input_id" ref="hr_payroll_rule_penalty"/>
+        </record>
+
+        <record id="hr_rule_input_dad" model="hr.rule.input">
+            <field name="code">DISCA</field>
+            <field name="name">Disciplinary Action</field>
+            <field name="input_id" ref="hr_payroll_rule_dad"/>
+        </record>
+        
+        <!-- Decimal Precision for intermediate payroll values -->
+        <record forcecreate="True" id="decimal_intermediate_payroll" model="decimal.precision">
+            <field name="name">Intermediate Payroll</field>
+            <field name="digits">6</field>
+        </record>
+
+        <!-- Additional Payslip Exceptions -->
+        
+        <record id="payslip_exception_net_salary_over" model="hr.payslip.exception.rule">
+            <field name="name">Net Salary exceeds previous payslip</field>
+            <field name="code">NETOVER</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = utils.PREVPS.exists and (categories.NET.amount > (utils.PREVPS.net + (utils.PREVPS.net * 0.20)))</field>
+            <field name="sequence" eval="30"/>
+            <field name="severity">high</field>
+        </record>
+        
+        <record id="payslip_exception_net_salary_under" model="hr.payslip.exception.rule">
+            <field name="name">Net Salary less than previous payslip</field>
+            <field name="code">NETUNDER</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = utils.PREVPS.exists and (categories.NET.amount &lt; (utils.PREVPS.net - (utils.PREVPS.net * 0.20)))</field>
+            <field name="sequence" eval="30"/>
+            <field name="severity">high</field>
+        </record>
+        
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'hr_payroll_extension/data/leave_types.xml'
--- hr_payroll_extension/data/leave_types.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_extension/data/leave_types.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        
+        <!-- Leave Types -->
+        
+        <record model="hr.holidays.status" id="holiday_status_bereavement">
+            <field name="name">Bereavement Leave</field>
+            <field name="code">LVBEREAVEMENT</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">blue</field>
+        </record>
+        
+        <record model="hr.holidays.status" id="holiday_status_civic">
+            <field name="name">Civic/Legal/Union Leave</field>
+            <field name="code">LVCIVIC</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">black</field>
+        </record>
+
+        <record model="hr.holidays.status" id="holiday_status_maternity">
+            <field name="name">Maternity/Paternity Leave</field>
+            <field name="code">LVMATERNITY</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">blue</field>
+        </record>
+        
+        <record model="hr.holidays.status" id="holiday_status_maternity_medical">
+            <field name="name">Maternity Medical Leave</field>
+            <field name="code">LVMMEDICAL</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">blue</field>
+        </record>
+        
+        <record model="hr.holidays.status" id="hr_holidays.holiday_status_sl">
+            <field name="name">Sick Leave</field>
+            <field name="code">LVSICK</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">magenta</field>
+        </record>
+
+        <record model="hr.holidays.status" id="hr_holidays.holiday_status_sl50">
+            <field name="name">Sick Leave at 50%</field>
+            <field name="code">LVSICK50</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">magenta</field>
+        </record>
+
+        <record model="hr.holidays.status" id="hr_holidays.holiday_status_sl00">
+            <field name="name">Sick Leave Without Pay</field>
+            <field name="code">LVSICK00</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">magenta</field>
+        </record>
+
+        <record model="hr.holidays.status" id="holiday_status_paid">
+            <field name="name">Paid Time Off</field>
+            <field name="code">LVPTO</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">blue</field>
+        </record>
+        
+        <record model="hr.holidays.status" id="holiday_status_wedding">
+            <field name="name">Wedding Leave</field>
+            <field name="code">LVWEDDING</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">blue</field>
+        </record>
+        
+        <record model="hr.holidays.status" id="holiday_status_unpaid">
+            <field name="name">Unpaid Time Off</field>
+            <field name="code">LVUTO</field>
+            <field name="limit" eval="True"/>
+            <field name="color_name">brown</field>
+        </record>
+        
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'hr_payroll_extension/hr_payroll.py'
--- hr_payroll_extension/hr_payroll.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_extension/hr_payroll.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,1514 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from pytz import timezone, utc
+
+import openerp.addons.decimal_precision as dp
+from datetime import datetime, timedelta
+
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DATEFORMAT
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as OE_DATETIMEFORMAT
+from openerp.tools.translate import _
+from openerp.osv import fields, osv
+
+
+class last_X_days:
+
+    """Last X Days
+    Keeps track of the days an employee worked/didn't work in the last X days.
+    """
+
+    def __init__(self, days=6):
+        self.limit = days
+        self.arr = []
+
+    def push(self, worked=False):
+        if len(self.arr) == self.limit:
+            self.arr.pop(0)
+        self.arr.append(worked)
+        return [v for v in self.arr]
+
+    def days_worked(self):
+        res = 0
+        for d in self.arr:
+            if d == True:
+                res += 1
+        return res
+
+
+class hr_payslip(osv.osv):
+
+    _name = 'hr.payslip'
+    _inherit = 'hr.payslip'
+
+    def _get_policy(self, policy_group, policy_ids, dDay):
+        "Return a policy with an effective date before dDay but greater than all others"
+
+        if not policy_group or not policy_ids:
+            return None
+
+        res = None
+        for policy in policy_ids:
+            dPolicy = datetime.strptime(policy.date, OE_DATEFORMAT).date()
+            if dPolicy <= dDay:
+                if res == None:
+                    res = policy
+                elif dPolicy > datetime.strptime(res.date, OE_DATEFORMAT).date():
+                    res = policy
+
+        return res
+
+    def _get_ot_policy(self, policy_group, dDay):
+        "Return an OT policy with an effective date before dDay but greater than all others"
+
+        return self._get_policy(policy_group, policy_group.ot_policy_ids, dDay)
+
+    def _get_absence_policy(self, policy_group, dDay):
+        "Return an Absence policy with an effective date before dDay but greater than all others"
+
+        return self._get_policy(policy_group, policy_group.absence_policy_ids, dDay)
+
+    def _get_presence_policy(self, policy_group, dDay):
+        "Return a Presence Policy with an effective date before dDay but greater than all others"
+
+        return self._get_policy(policy_group, policy_group.presence_policy_ids, dDay)
+
+    def _get_applied_time(self, worked_hours, pol_active_after, pol_duration=None):
+        '''Returns worked time in hours according to pol_active_after and pol_duration.'''
+
+        applied_min = (worked_hours * 60) - pol_active_after
+        if applied_min > 0.01:
+            applied_min = (pol_duration != None and applied_min >
+                           pol_duration) and pol_duration or applied_min
+        else:
+            applied_min = 0
+        applied_hours = float(applied_min) / 60.0
+        return applied_hours
+
+    def _book_holiday_hours(
+        self, cr, uid, contract, presence_policy, ot_policy, attendances,
+            holiday_obj, dtDay, rest_days, lsd, worked_hours, context=None):
+
+        done = False
+        push_lsd = True
+        hours = worked_hours
+
+        # Process normal working hours
+        for line in presence_policy.line_ids:
+            if line.type == 'holiday':
+                holiday_hours = self._get_applied_time(
+                    worked_hours, line.active_after,
+                    line.duration)
+                attendances[line.code]['number_of_hours'] += holiday_hours
+                attendances[line.code]['number_of_days'] += 1.0
+                hours -= holiday_hours
+                done = True
+
+        # Process OT hours
+        for line in ot_policy.line_ids:
+            if line.type == 'holiday':
+                ot_hours = self._get_applied_time(
+                    worked_hours, line.active_after)
+                attendances[line.code]['number_of_hours'] += ot_hours
+                attendances[line.code]['number_of_days'] += 1.0
+                hours -= ot_hours
+                done = True
+
+        if done and (dtDay.weekday() in rest_days or lsd.days_worked == 6):
+            # Mark this day as *not* worked so that subsequent days
+            # are not treated as over-time.
+            lsd.push(False)
+            push_lsd = False
+
+        if hours > -0.01 and hours < 0.01:
+            hours = 0
+        return hours, push_lsd
+
+    def _book_restday_hours(
+        self, cr, uid, contract, presence_policy, ot_policy, attendances,
+            dtDay, rest_days, lsd, worked_hours, context=None):
+
+        done = False
+        push_lsd = True
+        hours = worked_hours
+
+        # Process normal working hours
+        for line in presence_policy.line_ids:
+            if line.type == 'restday' and dtDay.weekday() in rest_days:
+                rd_hours = self._get_applied_time(
+                    worked_hours, line.active_after, line.duration)
+                attendances[line.code]['number_of_hours'] += rd_hours
+                attendances[line.code]['number_of_days'] += 1.0
+                hours -= rd_hours
+                done = True
+
+        # Process OT hours
+        for line in ot_policy.line_ids:
+            if line.type == 'restday' and dtDay.weekday() in rest_days:
+                ot_hours = self._get_applied_time(
+                    worked_hours, line.active_after)
+                attendances[line.code]['number_of_hours'] += ot_hours
+                attendances[line.code]['number_of_days'] += 1.0
+                hours -= ot_hours
+                done = True
+
+        if done and (dtDay.weekday() in rest_days or lsd.days_worked == 6):
+            # Mark this day as *not* worked so that subsequent days
+            # are not treated as over-time.
+            lsd.push(False)
+            push_lsd = False
+
+        if hours > -0.01 and hours < 0.01:
+            hours = 0
+        return hours, push_lsd
+
+    def _book_weekly_restday_hours(
+        self, cr, uid, contract, presence_policy, ot_policy, attendances,
+            dtDay, rest_days, lsd, worked_hours, context=None):
+
+        done = False
+        push_lsd = True
+        hours = worked_hours
+
+        # Process normal working hours
+        for line in presence_policy.line_ids:
+            if line.type == 'restday':
+                if lsd.days_worked() == line.active_after:
+                    rd_hours = self._get_applied_time(
+                        worked_hours, line.active_after, line.duration)
+                    attendances[line.code]['number_of_hours'] += rd_hours
+                    attendances[line.code]['number_of_days'] += 1.0
+                    hours -= rd_hours
+                    done = True
+
+        # Process OT hours
+        for line in ot_policy.line_ids:
+            if line.type == 'weekly' and line.weekly_working_days and line.weekly_working_days > 0:
+                if lsd.days_worked() == line.weekly_working_days:
+                    ot_hours = self._get_applied_time(
+                        worked_hours, line.active_after)
+                    attendances[line.code]['number_of_hours'] += ot_hours
+                    attendances[line.code]['number_of_days'] += 1.0
+                    hours -= ot_hours
+                    done = True
+
+        if done and (dtDay.weekday() in rest_days or lsd.days_worked == 6):
+            # Mark this day as *not* worked so that subsequent days
+            # are not treated as over-time.
+            lsd.push(False)
+            push_lsd = False
+
+        if hours > -0.01 and hours < 0.01:
+            hours = 0
+        return hours, push_lsd
+
+    def holidays_list_init(self, cr, uid, dFrom, dTo, context=None):
+
+        holiday_obj = self.pool.get('hr.holidays.public')
+        res = holiday_obj.get_holidays_list(
+            cr, uid, dFrom.year, context=context)
+        if dTo.year != dFrom.year:
+            res += holiday_obj.get_holidays_list(
+                cr, uid, dTo.year, context=context)
+        return res
+
+    def holidays_list_contains(self, d, holidays_list):
+
+        if d.strftime(OE_DATEFORMAT) in holidays_list:
+            return True
+        return False
+
+    def attendance_dict_init(self, cr, uid, contract, dFrom, dTo, context=None):
+
+        att_obj = self.pool.get('hr.attendance')
+
+        res = {}
+        att_list = att_obj.punches_list_init(
+            cr, uid, contract.employee_id.id, contract.pps_id,
+            dFrom, dTo, context=context)
+        res.update({'raw_list': att_list})
+        d = dFrom
+        while d <= dTo:
+            res[d.strftime(
+                OE_DATEFORMAT)] = att_obj.total_hours_on_day(
+                cr, uid, contract, d,
+                punches_list=att_list,
+                context=context)
+            d += timedelta(days=+1)
+
+        return res
+
+    def attendance_dict_hours_on_day(self, d, attendance_dict):
+
+        return attendance_dict[d.strftime(OE_DATEFORMAT)]
+
+    def attendance_dict_list(self, att_dict):
+
+        return att_dict['raw_list']
+
+    def leaves_list_init(self, cr, uid, employee_id, dFrom, dTo, tz, context=None):
+        '''Returns a list of tuples containing start, end dates for leaves within
+        the specified period.'''
+
+        leave_obj = self.pool.get('hr.holidays')
+        dtS = datetime.strptime(
+            dFrom.strftime(OE_DATEFORMAT) + ' 00:00:00', OE_DATETIMEFORMAT)
+        dtE = datetime.strptime(
+            dTo.strftime(OE_DATEFORMAT) + ' 23:59:59', OE_DATETIMEFORMAT)
+        utcdt_dayS = timezone(tz).localize(dtS).astimezone(utc)
+        utcdt_dayE = timezone(tz).localize(dtE).astimezone(utc)
+        utc_dayS = utcdt_dayS.strftime(OE_DATETIMEFORMAT)
+        utc_dayE = utcdt_dayE.strftime(OE_DATETIMEFORMAT)
+
+        leave_ids = leave_obj.search(
+            cr, uid, [('state', 'in', ['validate', 'validate1']),
+                      ('employee_id', '=', employee_id),
+                      ('type', '=', 'remove'),
+                      ('date_from', '<=', utc_dayE),
+                      ('date_to', '>=', utc_dayS)],
+            context=context)
+        res = []
+        if len(leave_ids) == 0:
+            return res
+
+        for leave in leave_obj.browse(cr, uid, leave_ids, context=context):
+            res.append({
+                'code': leave.holiday_status_id.code,
+                'tz': tz,
+                'start': utc.localize(datetime.strptime(leave.date_from, OE_DATETIMEFORMAT)),
+                'end': utc.localize(datetime.strptime(leave.date_to, OE_DATETIMEFORMAT))
+            })
+
+        return res
+
+    def leaves_list_get_hours(
+        self, cr, uid, employee_id, contract_id, sched_tpl_id, d,
+            leaves_list, context=None):
+        '''Return the number of hours of leave on a given date, d.'''
+
+        code = False
+        hours = 0
+        if len(leaves_list) == 0:
+            return code, hours
+
+        dtS = datetime.strptime(
+            d.strftime(OE_DATEFORMAT) + ' 00:00:00', OE_DATETIMEFORMAT)
+        dtE = datetime.strptime(
+            d.strftime(OE_DATEFORMAT) + ' 23:59:59', OE_DATETIMEFORMAT)
+        for l in leaves_list:
+            utcBegin = l['start']
+            utcEnd = l['end']
+            dtLvBegin = datetime.strptime(
+                utcBegin.strftime(OE_DATETIMEFORMAT), OE_DATETIMEFORMAT)
+            dtLvEnd = datetime.strptime(
+                utcEnd.strftime(OE_DATETIMEFORMAT), OE_DATETIMEFORMAT)
+            utcdt_dayS = timezone(l['tz']).localize(dtS).astimezone(utc)
+            utcdt_dayE = timezone(l['tz']).localize(dtE).astimezone(utc)
+            if utcdt_dayS <= utcEnd and utcdt_dayE >= utcBegin:
+                code = l['code']
+                sched_tpl_obj = self.pool.get('hr.schedule.template')
+                if utcBegin.date() < utcdt_dayS.date() and utcEnd.date() > utcdt_dayS.date():
+                    hours = 24
+                elif utcBegin.date() == utcdt_dayE.date():
+                    hours = float((utcdt_dayE - utcBegin).seconds / 60) / 60.0
+                elif utcBegin.date() == utcdt_dayS.date():
+                    shift_times = self.pool.get(
+                        'hr.schedule.detail').scheduled_begin_end_times(cr, uid,
+                                                                        employee_id,
+                                                                        contract_id,
+                                                                        dtS,
+                                                                        context=context)
+                    if len(shift_times) > 0:
+                        for dtStart, dtEnd in shift_times:
+                            if dtLvBegin < dtEnd:
+                                dt = (
+                                    dtLvBegin < dtStart) and dtStart or dtLvBegin
+                                hours += float(
+                                    (dtEnd - dt).seconds / 60) / 60.0
+                                dtLvBegin = dtEnd
+                    else:
+                        hours = sched_tpl_obj.get_hours_by_weekday(
+                            cr, uid, sched_tpl_id, d.weekday(),
+                            context=context) or 8
+                else:  # dtTo.date() == dToday
+                    shift_times = self.pool.get(
+                        'hr.schedule.detail').scheduled_begin_end_times(cr, uid,
+                                                                        employee_id,
+                                                                        contract_id,
+                                                                        dtS,
+                                                                        context=context)
+                    if len(shift_times) > 0:
+                        for dtStart, dtEnd in shift_times:
+                            if dtLvEnd > dtStart:
+                                dt = (dtLvEnd > dtEnd) and dtEnd or dtLvEnd
+                                hours += float(
+                                    (dt - dtStart).seconds / 60) / 60.0
+                    else:
+                        hours = sched_tpl_obj.get_hours_by_weekday(
+                            cr, uid, sched_tpl_id, d.weekday(),
+                            context=context) or 8
+
+        return code, hours
+
+    # Copied from addons/hr_payroll so that we can override worked days calculation to
+    # handle Overtime and absence
+    #
+    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
+        """
+
+        sched_tpl_obj = self.pool.get('hr.schedule.template')
+        sched_obj = self.pool.get('hr.schedule')
+        sched_detail_obj = self.pool.get('hr.schedule.detail')
+        ot_obj = self.pool.get('hr.policy.ot')
+        presence_obj = self.pool.get('hr.policy.presence')
+        absence_obj = self.pool.get('hr.policy.absence')
+        holiday_obj = self.pool.get('hr.holidays.public')
+
+        day_from = datetime.strptime(date_from, "%Y-%m-%d").date()
+        day_to = datetime.strptime(date_to, "%Y-%m-%d").date()
+        nb_of_days = (day_to - day_from).days + 1
+
+        # Initialize list of public holidays. We only need to calculate it once during
+        # the lifetime of this object so attach it directly to it.
+        #
+        try:
+            public_holidays_list = self._mtm_public_holidays_list
+        except AttributeError:
+            self._mtm_public_holidays_list = self.holidays_list_init(
+                cr, uid, day_from, day_to,
+                context=context)
+            public_holidays_list = self._mtm_public_holidays_list
+
+        def get_ot_policies(policy_group_id, day, data):
+
+            if data == None or not data['_reuse']:
+                data = {
+                    'policy': None,
+                    'daily': None,
+                    'restday2': None,
+                    'restday': None,
+                    'weekly': None,
+                    'holiday': None,
+                    '_reuse': False,
+                }
+            elif data['_reuse']:
+                return data
+
+            ot_policy = self._get_ot_policy(policy_group_id, day)
+            daily_ot = ot_policy and len(
+                ot_obj.daily_codes(cr, uid, ot_policy.id, context=context)) > 0 or None
+            restday2_ot = ot_policy and len(ot_obj.restday2_codes(
+                cr, uid, ot_policy.id, context=context)) > 0 or None
+            restday_ot = ot_policy and len(ot_obj.restday_codes(
+                cr, uid, ot_policy.id, context=context)) > 0 or None
+            weekly_ot = ot_policy and len(
+                ot_obj.weekly_codes(cr, uid, ot_policy.id, context=context)) > 0 or None
+            holiday_ot = ot_policy and len(ot_obj.holiday_codes(
+                cr, uid, ot_policy.id, context=context)) > 0 or None
+
+            data['policy'] = ot_policy
+            data['daily'] = daily_ot
+            data['restday2'] = restday2_ot
+            data['restday'] = restday_ot
+            data['weekly'] = weekly_ot
+            data['holiday'] = holiday_ot
+            return data
+
+        def get_absence_policies(policy_group_id, day, data):
+
+            if data == None or not data['_reuse']:
+                data = {
+                    'policy': None,
+                    '_reuse': False,
+                }
+            elif data['_reuse']:
+                return data
+
+            absence_policy = self._get_absence_policy(policy_group_id, day)
+
+            data['policy'] = absence_policy
+            return data
+
+        def get_presence_policies(policy_group_id, day, data):
+
+            if data == None or not data['_reuse']:
+                data = {
+                    'policy': None,
+                    '_reuse': False,
+                }
+            elif data['_reuse']:
+                return data
+
+            policy = self._get_presence_policy(policy_group_id, day)
+
+            data['policy'] = policy
+            return data
+
+        res = []
+        for contract in self.pool.get('hr.contract').browse(cr, uid, contract_ids, context=context):
+
+            worked_hours_in_week = 0
+
+            # Initialize list of leave's taken by the employee during the month
+            leaves_list = self.leaves_list_init(
+                cr, uid, contract.employee_id.id,
+                day_from, day_to, contract.pps_id.tz, context=context)
+
+            # Get default set of rest days for this employee/contract
+            contract_rest_days = sched_tpl_obj.get_rest_days(cr, uid,
+                                                             contract.schedule_template_id.id,
+                                                             context=context)
+
+            # Initialize dictionary of dates in this payslip and the hours the
+            # employee was scheduled to work on each
+            sched_hours_dict = sched_detail_obj.scheduled_begin_end_times_range(
+                cr, uid,
+                contract.employee_id.id,
+                contract.id,
+                day_from, day_to,
+                context=context)
+
+            # Initialize dictionary of hours worked per day
+            working_hours_dict = self.attendance_dict_init(
+                cr, uid, contract, day_from, day_to,
+                context=None)
+
+            # Short-circuit:
+            # If the policy for the first day is the same as the one for the
+            # last day assume that it will also be the same for the days in
+            # between, and reuse the same policy instead of checking for every day.
+            #
+            ot_data = None
+            data2 = None
+            ot_data = get_ot_policies(
+                contract.policy_group_id, day_from, ot_data)
+            data2 = get_ot_policies(contract.policy_group_id, day_to, data2)
+            if (ot_data['policy'] and data2['policy']) and ot_data['policy'].id == data2['policy'].id:
+                ot_data['_reuse'] = True
+
+            absence_data = None
+            data2 = None
+            absence_data = get_absence_policies(
+                contract.policy_group_id, day_from, absence_data)
+            data2 = get_absence_policies(
+                contract.policy_group_id, day_to, data2)
+            if (absence_data['policy'] and data2['policy']) and absence_data['policy'].id == data2['policy'].id:
+                absence_data['_reuse'] = True
+
+            presence_data = None
+            data2 = None
+            presence_data = get_presence_policies(
+                contract.policy_group_id, day_from, presence_data)
+            data2 = get_presence_policies(
+                contract.policy_group_id, day_to, data2)
+            if (presence_data['policy'] and data2['policy']) and presence_data['policy'].id == data2['policy'].id:
+                presence_data['_reuse'] = True
+
+            # Calculate the number of days worked in the last week of
+            # the previous month. Necessary to calculate Weekly Rest Day OT.
+            #
+            lsd = last_X_days()
+            att_obj = self.pool.get('hr.attendance')
+            att_ids = []
+            if len(lsd.arr) == 0:
+                d = day_from - timedelta(days=6)
+                while d < day_from:
+                    att_ids = att_obj.search(cr, uid, [
+                        ('employee_id', '=', contract.employee_id.id),
+                        ('day', '=', d.strftime(
+                            '%Y-%m-%d')),
+                    ],
+                        order='name',
+                        # XXX - necessary to keep
+                        # order: in,out,in,out,...
+                        context=context)
+                    if len(att_ids) > 1:
+                        lsd.push(True)
+                    else:
+                        lsd.push(False)
+                    d += timedelta(days=1)
+
+            attendances = {
+                'MAX': {
+                    'name': _("Maximum Possible Working Hours"),
+                    'sequence': 1,
+                    'code': 'MAX',
+                    'number_of_days': 0.0,
+                    'number_of_hours': 0.0,
+                    'contract_id': contract.id,
+                },
+            }
+            leaves = {}
+            att_obj = self.pool.get('hr.attendance')
+            awol_code = False
+            import logging
+            _l = logging.getLogger(__name__)
+            for day in range(0, nb_of_days):
+                dtDateTime = datetime.strptime(
+                    (day_from + timedelta(days=day)).strftime('%Y-%m-%d'), '%Y-%m-%d')
+                rest_days = contract_rest_days
+                normal_working_hours = 0
+
+                # Get Presence data
+                #
+                presence_data = get_presence_policies(
+                    contract.policy_group_id, dtDateTime.date(), presence_data)
+                presence_policy = presence_data['policy']
+                presence_codes = presence_policy and presence_obj.get_codes(
+                    cr, uid, presence_policy.id, context=context) or []
+                presence_sequence = 2
+
+                for pcode, pname, ptype, prate, pduration in presence_codes:
+                    if attendances.get(pcode, False):
+                        continue
+                    if ptype == 'normal':
+                        normal_working_hours += float(pduration) / 60.0
+                    attendances[pcode] = {
+                        'name': pname,
+                        'code': pcode,
+                        'sequence': presence_sequence,
+                        'number_of_days': 0.0,
+                        'number_of_hours': 0.0,
+                        'rate': prate,
+                        'contract_id': contract.id,
+                    }
+                    presence_sequence += 1
+
+                # Get OT data
+                #
+                ot_data = get_ot_policies(
+                    contract.policy_group_id, dtDateTime.date(), ot_data)
+                ot_policy = ot_data['policy']
+                daily_ot = ot_data['daily']
+                restday2_ot = ot_data['restday2']
+                restday_ot = ot_data['restday']
+                weekly_ot = ot_data['weekly']
+                ot_codes = ot_policy and ot_obj.get_codes(
+                    cr, uid, ot_policy.id, context=context) or []
+                ot_sequence = 3
+
+                for otcode, otname, ottype, otrate in ot_codes:
+                    if attendances.get(otcode, False):
+                        continue
+                    attendances[otcode] = {
+                        'name': otname,
+                        'code': otcode,
+                        'sequence': ot_sequence,
+                        'number_of_days': 0.0,
+                        'number_of_hours': 0.0,
+                        'rate': otrate,
+                        'contract_id': contract.id,
+                    }
+                    ot_sequence += 1
+
+                # Get Absence data
+                #
+                absence_data = get_absence_policies(
+                    contract.policy_group_id, dtDateTime.date(), absence_data)
+                absence_policy = absence_data['policy']
+                absence_codes = absence_policy and absence_obj.get_codes(
+                    cr, uid, absence_policy.id, context=context) or []
+                absence_sequence = 50
+
+                for abcode, abname, abtype, abrate, useawol in absence_codes:
+                    if leaves.get(abcode, False):
+                        continue
+                    if useawol:
+                        awol_code = abcode
+                    if abtype == 'unpaid':
+                        abrate = 0
+                    elif abtype == 'dock':
+                        abrate = -abrate
+                    leaves[abcode] = {
+                        'name': abname,
+                        'code': abcode,
+                        'sequence': absence_sequence,
+                        'number_of_days': 0.0,
+                        'number_of_hours': 0.0,
+                        'rate': abrate,
+                        'contract_id': contract.id,
+                    }
+                    absence_sequence += 1
+
+                # For Leave related computations:
+                #    actual_rest_days: days that are rest days in schedule that was actualy used
+                #    scheduled_hours: nominal number of full-time hours for the working day. If
+                #                     the employee is scheduled for this day we use those hours. If
+                #                     not we try to determine the hours he/she would have worked
+                #                     based on the schedule template attached to the contract.
+                #
+                actual_rest_days = sched_obj.get_rest_days(
+                    cr, uid, contract.employee_id.id,
+                    dtDateTime, context=context)
+                scheduled_hours = sched_detail_obj.scheduled_hours_on_day_from_range(
+                    dtDateTime.date(),
+                    sched_hours_dict)
+
+                # If the calculated rest days and actual rest days differ, use
+                # actual rest days
+                if actual_rest_days != None and len(rest_days) != len(actual_rest_days):
+                    rest_days = actual_rest_days
+                elif actual_rest_days != None:
+                    for d in actual_rest_days:
+                        if d not in rest_days:
+                            rest_days = actual_rest_days
+                            break
+
+                if scheduled_hours == 0 and dtDateTime.weekday() not in rest_days:
+                    scheduled_hours = sched_tpl_obj.get_hours_by_weekday(
+                        cr, uid, contract.schedule_template_id.id,
+                        dtDateTime.weekday(
+                        ),
+                        context=context)
+
+                # Actual number of hours worked on the day. Based on attendance
+                # records.
+                working_hours_on_day = self.attendance_dict_hours_on_day(
+                    dtDateTime.date(), working_hours_dict)
+
+                # Is today a holiday?
+                public_holiday = self.holidays_list_contains(
+                    dtDateTime.date(), public_holidays_list)
+
+                # Keep count of the number of hours worked during the week for
+                # weekly OT
+                if dtDateTime.weekday() == contract.pps_id.ot_week_startday:
+                    worked_hours_in_week = working_hours_on_day
+                else:
+                    worked_hours_in_week += working_hours_on_day
+
+                push_lsd = True
+                if working_hours_on_day:
+                    done = False
+
+                    if public_holiday:
+                        _hours, push_lsd = self._book_holiday_hours(
+                            cr, uid, contract, presence_policy, ot_policy, attendances,
+                            holiday_obj, dtDateTime, rest_days, lsd,
+                            working_hours_on_day, context=context)
+                        if _hours == 0:
+                            done = True
+                        else:
+                            working_hours_on_day = _hours
+
+                    if not done and restday2_ot:
+                        _hours, push_lsd = self._book_restday_hours(
+                            cr, uid, contract, presence_policy, ot_policy,
+                            attendances, dtDateTime, rest_days, lsd,
+                            working_hours_on_day, context=context)
+                        if _hours == 0:
+                            done = True
+                        else:
+                            working_hours_on_day = _hours
+
+                    if not done and restday_ot:
+                        _hours, push_lsd = self._book_weekly_restday_hours(
+                            cr, uid, contract, presence_policy, ot_policy,
+                            attendances, dtDateTime, rest_days, lsd,
+                            working_hours_on_day, context=context)
+                        if _hours == 0:
+                            done = True
+                        else:
+                            working_hours_on_day = _hours
+
+                    if not done and weekly_ot:
+                        for line in ot_policy.line_ids:
+                            if line.type == 'weekly' and (not line.weekly_working_days or line.weekly_working_days == 0):
+                                _active_after = float(line.active_after) / 60.0
+                                if worked_hours_in_week > _active_after:
+                                    if worked_hours_in_week - _active_after > working_hours_on_day:
+                                        attendances[line.code][
+                                            'number_of_hours'] += working_hours_on_day
+                                    else:
+                                        attendances[line.code][
+                                            'number_of_hours'] += worked_hours_in_week - _active_after
+                                    attendances[line.code][
+                                        'number_of_days'] += 1.0
+                                    done = True
+
+                    if not done and daily_ot:
+
+                        # Do the OT between specified times (partial OT) first, so that it
+                        # doesn't get double-counted in the regular OT.
+                        #
+                        partial_hr = 0
+                        hours_after_ot = working_hours_on_day
+                        for line in ot_policy.line_ids:
+                            active_after_hrs = float(line.active_after) / 60.0
+                            if line.type == 'daily' and working_hours_on_day > active_after_hrs and line.active_start_time:
+                                partial_hr = att_obj.partial_hours_on_day(
+                                    cr, uid, contract,
+                                    dtDateTime, active_after_hrs,
+                                    line.active_start_time,
+                                    line.active_end_time,
+                                    line.tz,
+                                    punches_list=self.attendance_dict_list(
+                                        working_hours_dict),
+                                    context=context)
+                                if partial_hr > 0:
+                                    attendances[line.code][
+                                        'number_of_hours'] += partial_hr
+                                    attendances[line.code][
+                                        'number_of_days'] += 1.0
+                                    hours_after_ot -= partial_hr
+
+                        for line in ot_policy.line_ids:
+                            active_after_hrs = float(line.active_after) / 60.0
+                            if line.type == 'daily' and hours_after_ot > active_after_hrs and not line.active_start_time:
+                                attendances[line.code][
+                                    'number_of_hours'] += hours_after_ot - (float(line.active_after) / 60.0)
+                                attendances[line.code]['number_of_days'] += 1.0
+
+                    if not done:
+                        for line in presence_policy.line_ids:
+                            if line.type == 'normal':
+                                normal_hours = self._get_applied_time(
+                                    working_hours_on_day,
+                                    line.active_after,
+                                    line.duration)
+                                attendances[line.code][
+                                    'number_of_hours'] += normal_hours
+                                attendances[line.code]['number_of_days'] += 1.0
+                                done = True
+                                _l.warning('nh: %s', normal_hours)
+                                _l.warning('att: %s', attendances[line.code])
+
+                    if push_lsd:
+                        lsd.push(True)
+                else:
+                    lsd.push(False)
+
+                leave_type, leave_hours = self.leaves_list_get_hours(
+                    cr, uid, contract.employee_id.id,
+                    contract.id, contract.schedule_template_id.id,
+                    day_from +
+                    timedelta(
+                        days=day),
+                    leaves_list, context=context)
+                if leave_type and (working_hours_on_day or scheduled_hours > 0 or dtDateTime.weekday() not in rest_days):
+                    if leave_type in leaves:
+                        leaves[leave_type]['number_of_days'] += 1.0
+                        leaves[leave_type]['number_of_hours'] += (
+                            leave_hours > scheduled_hours) and scheduled_hours or leave_hours
+                    else:
+                        leaves[leave_type] = {
+                            'name': leave_type,
+                            'sequence': 8,
+                            'code': leave_type,
+                            'number_of_days': 1.0,
+                            'number_of_hours': (leave_hours > scheduled_hours) and scheduled_hours or leave_hours,
+                            'contract_id': contract.id,
+                        }
+                elif awol_code and (scheduled_hours > 0 and working_hours_on_day < scheduled_hours) and not public_holiday:
+                    hours_diff = scheduled_hours - working_hours_on_day
+                    leaves[awol_code]['number_of_days'] += 1.0
+                    leaves[awol_code]['number_of_hours'] += hours_diff
+
+                # Calculate total possible working hours in the month
+                if dtDateTime.weekday() not in rest_days:
+                    attendances['MAX'][
+                        'number_of_hours'] += normal_working_hours
+                    attendances['MAX']['number_of_days'] += 1
+
+            leaves = [value for key, value in leaves.items()]
+            attendances = [value for key, value in attendances.items()]
+            res += attendances + leaves
+        return res
+
+    def _partial_period_factor(self, payslip, contract):
+
+        dpsFrom = datetime.strptime(payslip.date_from, OE_DATEFORMAT).date()
+        dpsTo = datetime.strptime(payslip.date_to, OE_DATEFORMAT).date()
+        dcStart = datetime.strptime(contract.date_start, OE_DATEFORMAT).date()
+        dcEnd = False
+        if (contract.date_end):
+            dcEnd = datetime.strptime(contract.date_end, OE_DATEFORMAT).date()
+
+        # both start and end of contract are out of the bounds of the payslip
+        if dcStart <= dpsFrom and (not dcEnd or dcEnd >= dpsTo):
+            return 1
+
+        # One or both start and end of contract are within the bounds of the payslip
+        #
+        nocontract_days = 0
+        if dcStart > dpsFrom:
+            nocontract_days += (dcStart - dpsFrom).days
+        if dcEnd and dcEnd < dpsTo:
+            nocontract_days += (dpsTo - dcEnd).days
+
+        total_days = (dpsTo - dpsFrom).days + 1
+        contract_days = total_days - nocontract_days
+        return (float(contract_days) / float(total_days))
+
+    def get_utilities_dict(self, cr, uid, contract, payslip, context=None):
+
+        res = {}
+        if not contract or not payslip:
+            return res
+
+        # Calculate percentage of pay period in which contract lies
+        res.update(
+            {'PPF': {'amount': self._partial_period_factor(payslip, contract)}})
+
+        # Calculate net amount of previous payslip
+        imd_obj = self.pool.get('ir.model.data')
+        ps_obj = self.pool.get('hr.payslip')
+        ps_ids = ps_obj.search(
+            cr, uid, [('employee_id', '=', contract.employee_id.id)],
+            order='date_from', context=context)
+        res.update({'PREVPS': {'exists': 0,
+                               'net': 0}
+                    })
+        if ps_ids > 0:
+            # Get database ID of Net salary category
+            res_model, net_id = imd_obj.get_object_reference(
+                cr, uid, 'hr_payroll', 'NET')
+
+            ps = ps_obj.browse(cr, uid, ps_ids[-1], context=context)
+            res['PREVPS']['exists'] = 1
+            total = 0
+            for line in ps.line_ids:
+                if line.salary_rule_id.category_id.id == net_id:
+                    total += line.total
+            res['PREVPS']['net'] = total
+
+        return res
+
+    # XXX
+    # Copied (almost) verbatim from hr_payroll for the sole purpose of adding the 'utils'
+    # object to localdict.
+    #
+    def get_payslip_lines(self, cr, uid, contract_ids, payslip_id, context):
+        def _sum_salary_rule_category(localdict, category, amount):
+            if category.parent_id:
+                localdict = _sum_salary_rule_category(
+                    localdict, category.parent_id, amount)
+            if category.code in localdict['categories'].dict:
+                localdict['categories'].dict[category.code] = localdict[
+                    'categories'].dict[category.code] + amount
+            else:
+                localdict['categories'].dict[category.code] = amount
+            return localdict
+
+        class BrowsableObject(object):
+
+            def __init__(self, pool, cr, uid, employee_id, dict):
+                self.pool = pool
+                self.cr = cr
+                self.uid = uid
+                self.employee_id = employee_id
+                self.dict = dict
+
+            def __getattr__(self, attr):
+                return attr in self.dict and self.dict.__getitem__(attr) or 0.0
+
+        class InputLine(BrowsableObject):
+
+            """a class that will be used into the python code, mainly for usability purposes"""
+
+            def sum(self, code, from_date, to_date=None):
+                if to_date is None:
+                    to_date = datetime.now().strftime('%Y-%m-%d')
+                result = 0.0
+                self.cr.execute("SELECT sum(amount) as sum\
+                            FROM hr_payslip as hp, hr_payslip_input as pi \
+                            WHERE hp.employee_id = %s AND hp.state = 'done' \
+                            AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s",
+                                (self.employee_id, from_date, to_date, code))
+                res = self.cr.fetchone()[0]
+                return res or 0.0
+
+        class WorkedDays(BrowsableObject):
+
+            """a class that will be used into the python code, mainly for usability purposes"""
+
+            def _sum(self, code, from_date, to_date=None):
+                if to_date is None:
+                    to_date = datetime.now().strftime('%Y-%m-%d')
+                result = 0.0
+                self.cr.execute("SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours\
+                            FROM hr_payslip as hp, hr_payslip_worked_days as pi \
+                            WHERE hp.employee_id = %s AND hp.state = 'done'\
+                            AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s",
+                                (self.employee_id, from_date, to_date, code))
+                return self.cr.fetchone()
+
+            def sum(self, code, from_date, to_date=None):
+                res = self._sum(code, from_date, to_date)
+                return res and res[0] or 0.0
+
+            def sum_hours(self, code, from_date, to_date=None):
+                res = self._sum(code, from_date, to_date)
+                return res and res[1] or 0.0
+
+        class Payslips(BrowsableObject):
+
+            """a class that will be used into the python code, mainly for usability purposes"""
+
+            def sum(self, code, from_date, to_date=None):
+                if to_date is None:
+                    to_date = datetime.now().strftime('%Y-%m-%d')
+                self.cr.execute("SELECT sum(case when hp.credit_note = False then (pl.total) else (-pl.total) end)\
+                            FROM hr_payslip as hp, hr_payslip_line as pl \
+                            WHERE hp.employee_id = %s AND hp.state = 'done' \
+                            AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s",
+                                (self.employee_id, from_date, to_date, code))
+                res = self.cr.fetchone()
+                return res and res[0] or 0.0
+
+        # we keep a dict with the result because a value can be overwritten by
+        # another rule with the same code
+        result_dict = {}
+        rules = {}
+        categories_dict = {}
+        blacklist = []
+        payslip_obj = self.pool.get('hr.payslip')
+        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:
+            inputs[input_line.code] = input_line
+
+        categories_obj = BrowsableObject(
+            self.pool, cr, uid, payslip.employee_id.id, categories_dict)
+        input_obj = InputLine(
+            self.pool, cr, uid, payslip.employee_id.id, inputs)
+        worked_days_obj = WorkedDays(
+            self.pool, cr, uid, payslip.employee_id.id, worked_days)
+        payslip_obj = Payslips(
+            self.pool, cr, uid, payslip.employee_id.id, payslip)
+        rules_obj = BrowsableObject(
+            self.pool, cr, uid, payslip.employee_id.id, rules)
+
+        localdict = {'categories': categories_obj, 'rules': rules_obj, 'payslip':
+                     payslip_obj, 'worked_days': worked_days_obj, 'inputs': input_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
+        rule_ids = self.pool.get('hr.payroll.structure').get_all_rules(
+            cr, uid, structure_ids, context=context)
+        # run the rules by sequence
+        sorted_rule_ids = [
+            id for id, sequence in sorted(rule_ids, key=lambda x:x[1])]
+
+        for contract in self.pool.get('hr.contract').browse(cr, uid, contract_ids, context=context):
+            temp_dict = {}
+            utils_dict = self.get_utilities_dict(
+                cr, uid, contract, payslip, context=context)
+            for k, v in utils_dict.iteritems():
+                k_obj = BrowsableObject(
+                    self.pool, cr, uid, payslip.employee_id.id, v)
+                temp_dict.update({k: k_obj})
+            utils_obj = BrowsableObject(
+                self.pool, cr, uid, payslip.employee_id.id, temp_dict)
+            employee = contract.employee_id
+            localdict.update(
+                {'employee': employee, 'contract': contract, 'utils': utils_obj})
+            for rule in obj_rule.browse(cr, uid, sorted_rule_ids, context=context):
+                key = rule.code + '-' + str(contract.id)
+                localdict['result'] = None
+                localdict['result_qty'] = 1.0
+                # check if the rule can be applied
+                if obj_rule.satisfy_condition(cr, uid, rule.id, localdict, context=context) and rule.id not in blacklist:
+                    # compute the amount of the rule
+                    amount, qty, rate = obj_rule.compute_rule(
+                        cr, uid, rule.id, localdict, context=context)
+                    # check if there is already a rule computed with that code
+                    previous_amount = rule.code in localdict and localdict[
+                        rule.code] or 0.0
+                    # set/overwrite the amount computed for this rule in the
+                    # localdict
+                    tot_rule = amount * qty * rate / 100.0
+                    localdict[rule.code] = tot_rule
+                    rules[rule.code] = rule
+                    # sum the amount for its salary category
+                    localdict = _sum_salary_rule_category(
+                        localdict, rule.category_id, tot_rule - previous_amount)
+                    # create/overwrite the rule in the temporary results
+                    result_dict[key] = {
+                        'salary_rule_id': rule.id,
+                        'contract_id': contract.id,
+                        'name': rule.name,
+                        'code': rule.code,
+                        'category_id': rule.category_id.id,
+                        'sequence': rule.sequence,
+                        'appears_on_payslip': rule.appears_on_payslip,
+                        'condition_select': rule.condition_select,
+                        'condition_python': rule.condition_python,
+                        'condition_range': rule.condition_range,
+                        'condition_range_min': rule.condition_range_min,
+                        'condition_range_max': rule.condition_range_max,
+                        'amount_select': rule.amount_select,
+                        'amount_fix': rule.amount_fix,
+                        'amount_python_compute': rule.amount_python_compute,
+                        'amount_percentage': rule.amount_percentage,
+                        'amount_percentage_base': rule.amount_percentage_base,
+                        'register_id': rule.register_id.id,
+                        'amount': amount,
+                        'employee_id': contract.employee_id.id,
+                        'quantity': qty,
+                        'rate': rate,
+                    }
+                else:
+                    # blacklist this rule and its children
+                    blacklist += [id for id, seq in self.pool.get(
+                        'hr.salary.rule')._recursive_search_of_rules(cr, uid, [rule], context=context)]
+
+        result = [value for code, value in result_dict.items()]
+        return result
+
+hr_payslip()
+
+
+class hr_payslip_line(osv.osv):
+
+    _name = 'hr.payslip.line'
+    _inherit = 'hr.payslip.line'
+
+    _columns = {
+        'amount': fields.float('Amount', digits_compute=dp.get_precision('Intermediate Payroll')),
+    }
+
+
+class hr_attendance(osv.osv):
+
+    _name = 'hr.attendance'
+    _inherit = 'hr.attendance'
+
+    def _calculate_rollover(self, utcdt, rollover_hours):
+
+        # XXX - assume time part of utcdt is already set to midnight
+        return utcdt + timedelta(hours=int(rollover_hours))
+
+    def punches_list_init(self, cr, uid, employee_id, pps_template, dFrom, dTo, context=None):
+        '''Returns a dict containing a key for each day in range dFrom - dToday and a
+        corresponding tuple containing two list: in punches, and the corresponding out punches'''
+
+        res = []
+
+        # Convert datetime to tz aware datetime according to tz in pay period schedule,
+        # then to UTC, and then to naive datetime for comparison with values in db.
+        #
+        # Also, includue records 48 hours previous to and 48 hours after the desired
+        # dates so that any requests for rollover, sessions, etc are can be satisfied
+        #
+        dtFrom = datetime.strptime(
+            dFrom.strftime(OE_DATEFORMAT) + ' 00:00:00', OE_DATETIMEFORMAT)
+        dtFrom += timedelta(hours=-48)
+        dtTo = datetime.strptime(
+            dTo.strftime(OE_DATEFORMAT) + ' 00:00:00', OE_DATETIMEFORMAT)
+        dtTo += timedelta(hours=+48)
+        utcdtFrom = timezone(pps_template.tz).localize(
+            dtFrom, is_dst=False).astimezone(utc)
+        utcdtTo = timezone(pps_template.tz).localize(
+            dtTo, is_dst=False).astimezone(utc)
+        utcdtDay = utcdtFrom
+        utcdtDayEnd = utcdtTo + timedelta(days=+1, seconds=-1)
+        ndtDay = utcdtDay.replace(tzinfo=None)
+        ndtDayEnd = utcdtDayEnd.replace(tzinfo=None)
+
+        ids = self.search(cr, uid, [('employee_id', '=', employee_id),
+                                    '&', (
+                                        'name', '>=', ndtDay.strftime(OE_DATETIMEFORMAT)),
+                                    ('name', '<=', ndtDayEnd.strftime(OE_DATETIMEFORMAT))],
+                          order='name', context=context)
+
+        for a in self.browse(cr, uid, ids, context=context):
+            res.append((a.action, a.name))
+
+        return res
+
+    def punches_list_search(self, cr, uid, ndtFrom, ndtTo, punches_list, context=None):
+
+        res = []
+        for action, name in punches_list:
+            ndtName = datetime.strptime(name, OE_DATETIMEFORMAT)
+            if ndtName >= ndtFrom and ndtName <= ndtTo:
+                res.append((action, name))
+        return res
+
+    def _get_normalized_punches(self, cr, uid, employee_id, pps_template, dDay, punches_list, context=None):
+        '''Returns a tuple containing two lists: in punches, and corresponding out punches'''
+
+        #
+        # We assume that:
+        #    - No dangling sign-in or sign-out
+        #
+
+        # Convert datetime to tz aware datetime according to tz in pay period schedule,
+        # then to UTC, and then to naive datetime for comparison with values in db.
+        #
+        dt = datetime.strptime(
+            dDay.strftime(OE_DATEFORMAT) + ' 00:00:00', OE_DATETIMEFORMAT)
+        utcdtDay = timezone(pps_template.tz).localize(
+            dt, is_dst=False).astimezone(utc)
+        utcdtDayEnd = utcdtDay + timedelta(days=+1, seconds=-1)
+        ndtDay = utcdtDay.replace(tzinfo=None)
+        ndtDayEnd = utcdtDayEnd.replace(tzinfo=None)
+        my_list = self.punches_list_search(
+            cr, uid, ndtDay, ndtDayEnd, punches_list, context=context)
+        if len(my_list) == 0:
+            return [], []
+
+        # We are assuming attendances are normalized: (in, out, in, out, ...)
+        sin = []
+        sout = []
+        for action, name in my_list:
+            if action == 'sign_in':
+                sin.append(name)
+            elif action == 'sign_out':
+                sout.append(name)
+
+        if len(sin) == 0 and len(sout) == 0:
+            return [], []
+
+        # CHECKS AT THE START OF THE DAY
+        # Remove sessions that would have been included in yesterday's
+        # attendance.
+
+        # We may have a a session *FROM YESTERDAY* that crossed-over into
+        # today. If it is greater than the maximum continuous hours allowed into
+        # the next day (as configured in the pay period schedule), then count
+        # only the difference between the actual and the maximum continuous
+        # hours.
+        #
+        dtRollover = (self._calculate_rollover(
+            utcdtDay, pps_template.ot_max_rollover_hours)).replace(tzinfo=None)
+        if (len(sout) - len(sin)) == 0:
+
+            if len(sout) > 0:
+                dtSout = datetime.strptime(sout[0], OE_DATETIMEFORMAT)
+                dtSin = datetime.strptime(sin[0], OE_DATETIMEFORMAT)
+                if dtSout > dtRollover and (dtSout < dtSin):
+                    sin = [dtRollover.strftime(OE_DATETIMEFORMAT)] + sin
+                elif dtSout < dtSin:
+                    sout = sout[1:]
+                    # There may be another session that starts within the
+                    # rollover period
+                    if dtSin < dtRollover and float((dtSin - dtSout).seconds) / 60.0 >= pps_template.ot_max_rollover_gap:
+                        sin = sin[1:]
+                        sout = sout[1:]
+            else:
+                return [], []
+        elif (len(sout) - len(sin)) == 1:
+            dtSout = datetime.strptime(sout[0], OE_DATETIMEFORMAT)
+            if dtSout > dtRollover:
+                sin = [dtRollover.strftime(OE_DATETIMEFORMAT)] + sin
+            else:
+                sout = sout[1:]
+                # There may be another session that starts within the rollover
+                # period
+                dtSin = False
+                if len(sin) > 0:
+                    dtSin = datetime.strptime(sin[0], OE_DATETIMEFORMAT)
+                if dtSin and dtSin < dtRollover and float((dtSin - dtSout).seconds) / 60.0 >= pps_template.ot_max_rollover_gap:
+                    sin = sin[1:]
+                    sout = sout[1:]
+
+        # If the first sign-in was within the rollover gap *AT* midnight check to
+        # see if there are any sessions within the rollover gap before it.
+        #
+        if len(sout) > 0:
+            ndtSin = datetime.strptime(sin[0], OE_DATETIMEFORMAT)
+            if (ndtSin - timedelta(minutes=pps_template.ot_max_rollover_gap)) <= ndtDay:
+                my_list4 = self.punches_list_search(
+                    cr, uid, ndtDay + timedelta(hours=-24),
+                    ndtDay + timedelta(seconds=-1), punches_list, context=context)
+                if len(my_list4) > 0:
+                    if (my_list4[-1].action == 'sign_out'):
+                        ndtSout = datetime.strptime(
+                            my_list4[-1].name, OE_DATETIMEFORMAT)
+                        if (ndtSin <= ndtSout + timedelta(minutes=pps_template.ot_max_rollover_gap)):
+                            sin = sin[1:]
+                            sout = sout[1:]
+
+        # CHECKS AT THE END OF THE DAY
+        # Include sessions from tomorrow that should be included in today's
+        # attendance.
+
+        # We may have a session that crosses the midnight boundary. If so, add it to today's
+        # session.
+        #
+        dtRollover = (self._calculate_rollover(ndtDay + timedelta(days=1),
+                                               pps_template.ot_max_rollover_hours)).replace(tzinfo=None)
+        if (len(sin) - len(sout)) == 1:
+
+            my_list2 = self.punches_list_search(
+                cr, uid, ndtDayEnd + timedelta(seconds=+1),
+                ndtDayEnd + timedelta(days=1), punches_list, context=context)
+            if len(my_list2) == 0:
+                name = self.pool.get('hr.employee').read(
+                    cr, uid, employee_id, ['name'])['name']
+                raise osv.except_osv(_('Attendance Error!'),
+                                     _('There is not a final sign-out record for %s on %s') % (name, dDay))
+
+            action, name = my_list2[0]
+            if action == 'sign_out':
+                dtSout = datetime.strptime(name, OE_DATETIMEFORMAT)
+                if dtSout > dtRollover:
+                    sout.append(dtRollover.strftime(OE_DATETIMEFORMAT))
+                else:
+                    sout.append(name)
+                    # There may be another session within the OT max. rollover
+                    # gap
+                    if len(my_list2) > 2 and my_list2[1][0] == 'sign_in':
+                        dtSin = datetime.strptime(name, OE_DATETIMEFORMAT)
+                        if float((dtSin - dtSout).seconds) / 60.0 < pps_template.ot_max_rollover_gap:
+                            sin.append(my_list2[1][1])
+                            sout.append(my_list2[2][1])
+
+            else:
+                name = self.pool.get('hr.employee').read(
+                    cr, uid, employee_id, ['name'])['name']
+                raise osv.except_osv(_('Attendance Error!'),
+                                     _('There is a sign-in with no corresponding sign-out for %s on %s') % (name, dDay))
+
+        # If the last sign-out was within the rollover gap *BEFORE* midnight check to
+        # see if there are any sessions within the rollover gap after it.
+        #
+        if len(sout) > 0:
+            ndtSout = datetime.strptime(sout[-1], OE_DATETIMEFORMAT)
+            if (ndtDayEnd - timedelta(minutes=pps_template.ot_max_rollover_gap)) <= ndtSout:
+                my_list3 = self.punches_list_search(
+                    cr, uid, ndtDayEnd + timedelta(seconds=+1),
+                    ndtDayEnd + timedelta(hours=+24), punches_list, context=context)
+                if len(my_list3) > 0:
+                    action, name = my_list3[0]
+                    ndtSin = datetime.strptime(name, OE_DATETIMEFORMAT)
+                    if (ndtSin <= ndtSout + timedelta(minutes=pps_template.ot_max_rollover_gap)) and action == 'sign_in':
+                        sin.append(name)
+                        sout.append(my_list3[1][1])
+
+        return sin, sout
+
+    def _on_day(self, cr, uid, contract, dDay, punches_list=None, context=None):
+        '''Return two lists: the first is sign-in times, and the second is corresponding sign-outs.'''
+
+        if punches_list == None:
+            punches_list = self.punches_list_init(
+                cr, uid, contract.employee_id.id, contract.pps_id,
+                dDay, dDay, context)
+
+        sin, sout = self._get_normalized_punches(
+            cr, uid, contract.employee_id.id, contract.pps_id,
+            dDay, punches_list, context=context)
+        if len(sin) != len(sout):
+            raise osv.except_osv(
+                _('Number of Sign-in and Sign-out records do not match!'),
+                _('Employee: %s\nSign-in(s): %s\nSign-out(s): %s') % (contract.employee_id.name, sin, sout))
+
+        return sin, sout
+
+    def punch_names_on_day(self, cr, uid, contract, dDay, punches_list=None, context=None):
+        '''Return a list of tuples containing in and corresponding out punches for the day.'''
+
+        sin, sout = self._on_day(
+            cr, uid, contract, dDay, punches_list=punches_list, context=context)
+
+        res = []
+        for i in range(0, len(sin)):
+            res.append((sin[i], sout[i]))
+
+        return res
+
+    def punch_ids_on_day(self, cr, uid, contract, dDay, punches_list=None, context=None):
+        '''Return a list of database ids of punches for the day.'''
+
+        sin, sout = self._on_day(
+            cr, uid, contract, dDay, punches_list=punches_list, context=context)
+
+        names = []
+        for i in range(0, len(sin)):
+            names.append(sin[i])
+            names.append(sout[i])
+
+        return self.search(
+            cr, uid, [('employee_id', '=', contract.employee_id.id),
+                      ('name', 'in', names)],
+            order='name', context=context)
+
+    def total_hours_on_day(self, cr, uid, contract, dDay, punches_list=None, context=None):
+        '''Calculate the number of hours worked on specified date.'''
+
+        sin, sout = self._on_day(
+            cr, uid, contract, dDay, punches_list=punches_list, context=context)
+
+        worked_hours = 0
+        for i in range(0, len(sin)):
+            start = datetime.strptime(sin[i], '%Y-%m-%d %H:%M:%S')
+            end = datetime.strptime(sout[i], '%Y-%m-%d %H:%M:%S')
+            worked_hours += float((end - start).seconds) / 60.0 / 60.0
+
+        return worked_hours
+
+    def partial_hours_on_day(
+        self, cr, uid, contract, dtDay, active_after, begin, stop, tz,
+            punches_list=None, context=None):
+        '''Calculate the number of hours worked between begin and stop hours, but
+        after active_after hours past the beginning of the first sign-in on specified date.'''
+
+        # Since OpenERP stores datetime in db as UTC, but in naive format we have to do
+        # the following to compare our partial time to the time in db:
+        #    1. Make our partial time into a naive datetime
+        #    2. Localize the naive datetime to the timezone specified by our caller
+        #    3. Convert our localized datetime to UTC
+        #    4. Convert our UTC datetime back into naive datetime format
+        #
+        dtBegin = datetime.strptime(
+            dtDay.strftime(OE_DATEFORMAT) + ' ' + begin + ':00', OE_DATETIMEFORMAT)
+        dtStop = datetime.strptime(
+            dtDay.strftime(OE_DATEFORMAT) + ' ' + stop + ':00', OE_DATETIMEFORMAT)
+        if dtStop <= dtBegin:
+            dtStop += timedelta(days=1)
+        utcdtBegin = timezone(tz).localize(
+            dtBegin, is_dst=False).astimezone(utc)
+        utcdtStop = timezone(tz).localize(dtStop, is_dst=False).astimezone(utc)
+        dtBegin = utcdtBegin.replace(tzinfo=None)
+        dtStop = utcdtStop.replace(tzinfo=None)
+
+        if punches_list == None:
+            punches_list = self.punches_list_init(
+                cr, uid, contract.employee_id.id, contract.pps_id,
+                dtDay.date(), dtDay.date(), context)
+        sin, sout = self._get_normalized_punches(
+            cr, uid, contract.employee_id.id, contract.pps_id,
+            dtDay.date(), punches_list, context=context)
+
+        worked_hours = 0
+        lead_hours = 0
+        for i in range(0, len(sin)):
+            start = datetime.strptime(sin[i], '%Y-%m-%d %H:%M:%S')
+            end = datetime.strptime(sout[i], '%Y-%m-%d %H:%M:%S')
+            if worked_hours == 0 and end <= dtBegin:
+                lead_hours += float((end - start).seconds) / 60.0 / 60.0
+            elif worked_hours == 0 and end > dtBegin:
+                if start < dtBegin:
+                    lead_hours += float(
+                        (dtBegin - start).seconds) / 60.0 / 60.0
+                    start = dtBegin
+                if end > dtStop:
+                    end = dtStop
+                worked_hours = float((end - start).seconds) / 60.0 / 60.0
+            elif worked_hours > 0 and start < dtStop:
+                if end > dtStop:
+                    end = dtStop
+                worked_hours += float((end - start).seconds) / 60.0 / 60.0
+
+        if worked_hours == 0:
+            return 0
+        elif lead_hours >= active_after:
+            return worked_hours
+
+        return max(0, (worked_hours + lead_hours) - active_after)
+
+
+class hr_contract(osv.osv):
+
+    _name = 'hr.contract'
+    _inherit = 'hr.contract'
+
+    def _hourly(self, cr, uid, ids, field_name, args, context=None):
+
+        res = {}
+        for contract in self.browse(cr, uid, ids, context=context):
+            rate = 0.0
+            if contract.wage_type == 'hourly':
+                rate = contract.wage
+            elif contract.wage_type == 'daily':
+                rate = contract.wage / 8.0
+            elif contract.wage_type == 'salary':
+                rate = contract.wage / 26.0 / 8.0
+            res[contract.id] = rate
+        return res
+
+    def _daily(self, cr, uid, ids, field_name, args, context=None):
+
+        res = {}
+        for contract in self.browse(cr, uid, ids, context=context):
+            rate = 0.0
+            if contract.wage_type == 'hourly':
+                rate = contract.wage * 8.0
+            elif contract.wage_type == 'daily':
+                rate = contract.wage
+            elif contract.wage_type == 'salary':
+                rate = contract.wage / 26.0
+            res[contract.id] = rate
+        return res
+
+    def _monthly(self, cr, uid, ids, field_name, args, context=None):
+
+        res = {}
+        for contract in self.browse(cr, uid, ids, context=context):
+            rate = 0.0
+            if contract.wage_type == 'hourly':
+                rate = contract.wage * 8.0 * 26.0
+            elif contract.wage_type == 'daily':
+                rate = contract.wage * 26
+            elif contract.wage_type == 'salary':
+                rate = contract.wage
+            res[contract.id] = rate
+        return res
+
+    _columns = {
+        'wage_type': fields.selection((('hourly', 'Hourly'),
+                                       ('daily', 'Daily'),
+                                       ('salary', 'Salary')),
+                                      'Wage Type', required=True),
+        'wage_hourly': fields.function(_hourly, type='float', digits_compute=dp.get_precision('Intermediate Payroll'), string='Hourly Wages'),
+        'wage_daily': fields.function(_daily, type='float', digits_compute=dp.get_precision('Intermediate Payroll'), string='Daily Wages'),
+        'wage_monthly': fields.function(_monthly, type='float', digits_compute=dp.get_precision('Intermediate Payroll'), string='Monthly Wages'),
+    }
+
+    _defaults = {
+        'wage_type': 'salary',
+    }
+
+
+class hr_salary_rule(osv.Model):
+
+    _name = 'hr.salary.rule'
+    _inherit = 'hr.salary.rule'
+
+    _columns = {
+        'quantity': fields.char('Quantity', size=512, 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."),
+    }
+
+
+class hr_payslip_worked_days(osv.Model):
+
+    _name = 'hr.payslip.worked_days'
+    _inherit = 'hr.payslip.worked_days'
+
+    _columns = {
+        'rate': fields.float('Rate', required=True),
+    }
+
+    _defaults = {
+        'rate': 0.0,
+    }

=== added file 'hr_payroll_extension/hr_payroll_view.xml'
--- hr_payroll_extension/hr_payroll_view.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_extension/hr_payroll_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record id="view_salary_rule_form" model="ir.ui.view">
+            <field name="name">hr.salary.rule.form.inherit</field>
+            <field name="model">hr.salary.rule</field>
+            <field name="inherit_id" ref="hr_payroll.hr_salary_rule_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='quantity']" position="replace">
+                        <field name="quantity" attrs="{'required':[('amount_select','!=','code')]}"/><newline/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+    
+    </data>
+</openerp>

=== added directory 'hr_payroll_extension/src'
=== added directory 'hr_payroll_extension/src/static'
=== added directory 'hr_payroll_extension/src/static/src'
=== added directory 'hr_payroll_extension/src/static/src/img'
=== added file 'hr_payroll_extension/src/static/src/img/icon.png'
Binary files hr_payroll_extension/src/static/src/img/icon.png	1970-01-01 00:00:00 +0000 and hr_payroll_extension/src/static/src/img/icon.png	2013-09-27 21:15:53 +0000 differ
=== added directory 'hr_payroll_extension/static'
=== added directory 'hr_payroll_extension/static/src'
=== added directory 'hr_payroll_extension/static/src/img'
=== added file 'hr_payroll_extension/static/src/img/icon.png'
Binary files hr_payroll_extension/static/src/img/icon.png	1970-01-01 00:00:00 +0000 and hr_payroll_extension/static/src/img/icon.png	2013-09-27 21:15:53 +0000 differ
=== added directory 'hr_payroll_period'
=== added file 'hr_payroll_period/__init__.py'
--- hr_payroll_period/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,24 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import hr_attendance
+from . import hr_payroll_period
+from . import wizard

=== added file 'hr_payroll_period/__openerp__.py'
--- hr_payroll_period/__openerp__.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/__openerp__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,68 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{
+    'name': 'Payroll Period',
+    'version': '1.0',
+    'category': 'Generic Modules/Human Resources',
+    'description': """
+Easy Payroll Management
+=======================
+This module implements a more formal payroll cycle. This cycle is based on payroll
+period schedules configured by the user. An end-of-pay-period wizard guides the
+HR officer or manager through the payroll process. For each payroll period a specific set
+of criteria have to be met in order to proceed to the next stage of the process. For
+example:
+    - Attendance records are complete
+    """,
+    'author': 'Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>',
+    'website': 'http://miketelahun.wordpress.com',
+    'depends': [
+        'hr_contract',
+        'hr_contract_init',
+        'hr_employee_state',
+        'hr_payroll',
+        'hr_payroll_register',
+        'hr_payslip_amendment',
+        'hr_public_holidays',
+        'hr_schedule',
+        'hr_security',
+    ],
+    'init_xml': [
+    ],
+    'update_xml': [
+        'security/ir.model.access.csv',
+        'security/ir_rule.xml',
+        'data/hr_payroll_period_data.xml',
+        'data/mail_group_data.xml',
+        'wizard/payroll_period_end_view.xml',
+        'hr_payroll_period_view.xml',
+        'hr_attendance_workflow.xml',
+        'hr_payroll_period_workflow.xml',
+        'hr_payroll_period_cron.xml',
+    ],
+    'test': [
+    ],
+    'demo_xml': [
+    ],
+    'installable': True,
+    'active': False,
+}

=== added directory 'hr_payroll_period/data'
=== added file 'hr_payroll_period/data/hr_payroll_period_data.xml'
--- hr_payroll_period/data/hr_payroll_period_data.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/data/hr_payroll_period_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        
+        <!-- Messaging / Chatter -->
+        
+        <record id="mt_state_open" model="mail.message.subtype">
+            <field name="name">Create</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="description">Payroll Period opened</field>
+        </record>
+        <record id="mt_state_end" model="mail.message.subtype">
+            <field name="name">Ended</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="description">Begin end-of-period processing</field>
+        </record>
+        <record id="mt_state_lock" model="mail.message.subtype">
+            <field name="name">Locked</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="description">Payroll Period has been locked</field>
+        </record>
+        <record id="mt_state_generate" model="mail.message.subtype">
+            <field name="name">Generate Payslips</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="description">Pay Slip generation has begun</field>
+        </record>
+        <record id="mt_state_payment" model="mail.message.subtype">
+            <field name="name">Payment</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="description">Payment has started</field>
+        </record>
+        <record id="mt_state_close" model="mail.message.subtype">
+            <field name="name">Closed</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="description">Payroll Period closed</field>
+        </record>
+        
+        <!-- Payroll Exception Rules -->
+        
+        <record id="payslip_exception_third" model="hr.payslip.exception.rule">
+            <field name="name">Net Salary is less than 1/3 of Gross</field>
+            <field name="code">NET13GROSS</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.NET.amount &lt; (categories.GROSS.amount / 3.0)</field>
+            <field name="sequence" eval="10"/>
+            <field name="severity">critical</field>
+            <field name="note">In some jurisdictions the employee's Net Salary cannot be less than 1/3 of Gross.</field>
+        </record>
+        
+        <record id="payslip_exception_negative" model="hr.payslip.exception.rule">
+            <field name="name">Net Salary is negative</field>
+            <field name="code">NETNEG</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.NET.amount &lt;= -0.01</field>
+            <field name="sequence" eval="15"/>
+            <field name="severity">critical</field>
+            <field name="note">An employee can never have a negative salary.</field>
+        </record>
+        
+        <record id="payslip_exception_zero" model="hr.payslip.exception.rule">
+            <field name="name">Net Salary is Zero</field>
+            <field name="code">NETZERO</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = categories.NET.amount > -0.01 and categories.NET.amount &lt; 0.01</field>
+            <field name="sequence" eval="20"/>
+            <field name="severity">low</field>
+        </record>
+        
+        <record id="payslip_exception_ot2basic_ratio" model="hr.payslip.exception.rule">
+            <field name="name">OverTime / Basic Salary ratio threshold exceeded</field>
+            <field name="code">OTBSR</field>
+            <field name="condition_select">python</field>
+            <field name="condition_python">result = (categories.OT / categories.BASIC.amount) > 0.5</field>
+            <field name="sequence" eval="25"/>
+            <field name="severity">high</field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_payroll_period/data/mail_group_data.xml'
--- hr_payroll_period/data/mail_group_data.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/data/mail_group_data.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+        
+        <!-- Main HR Working Group -->
+        <record model="mail.group" id="mail_group_hr_main">
+            <field name="name">HR Information</field>
+            <field name="description">Main point contact for HR related events, actions and discussions.</field>
+        </record>
+        
+    </data>
+</openerp>
\ No newline at end of file

=== added file 'hr_payroll_period/hr_attendance.py'
--- hr_payroll_period/hr_attendance.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/hr_attendance.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,99 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from openerp.tools.translate import _
+from osv import fields, osv
+
+
+class hr_attendance(osv.osv):
+
+    _name = 'hr.attendance'
+    _inherit = 'hr.attendance'
+
+    _columns = {
+        'state': fields.selection((
+            ('draft', 'Unverified'), (
+                'verified', 'Verified'), ('locked', 'Locked'),
+        ), 'State', required=True, readonly=True),
+    }
+
+    _defaults = {
+        'state': 'draft',
+    }
+
+    def is_locked(self, cr, uid, employee_id, utcdt_str, context=None):
+
+        res = False
+        pp_obj = self.pool.get('hr.payroll.period')
+        ee_data = self.pool.get('hr.employee').read(cr, uid, employee_id,
+                                                    ['contract_ids'], context=context)
+        pp_ids = pp_obj.search(cr, uid, [
+            ('state', 'in', [
+                'locked', 'generate', 'payment', 'closed']),
+            '&', ('date_start', '<=', utcdt_str),
+            ('date_end', '>=', utcdt_str),
+        ], context=context)
+        for pp in pp_obj.browse(cr, uid, pp_ids, context=context):
+            pp_contract_ids = [c.id for c in pp.schedule_id.contract_ids]
+            for c_id in ee_data['contract_ids']:
+                if c_id in pp_contract_ids:
+                    res = True
+                    break
+            if res == True:
+                break
+
+        return res
+
+    def create(self, cr, uid, vals, context=None):
+
+        if self.is_locked(cr, uid, vals['employee_id'], vals['name'], context=context):
+            ee_data = self.pool.get(
+                'hr.employee').read(cr, uid, vals['employee_id'], ['name'],
+                                    context=context)
+            raise osv.except_osv(_('The period is Locked!'),
+                                 _('You may not add an attendace record to a locked period.\nEmployee: %s\nTime: %s') % (ee_data['name'], vals['name']))
+
+        return super(hr_attendance, self).create(cr, uid, vals, context=context)
+
+    def unlink(self, cr, uid, ids, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        for punch in self.browse(cr, uid, ids, context=context):
+            if punch.state in ['verified', 'locked']:
+                raise osv.except_osv(_('The Record cannot be deleted!'),
+                                     _('You may not delete a record that is in a %s state:\nEmployee: %s, Date: %s, Action: %s') % (punch.state, punch.employee_id.name, punch.name, punch.action))
+
+        return super(hr_attendance, self).unlink(cr, uid, ids, context=context)
+
+    def write(self, cr, uid, ids, vals, context=None):
+
+        if isinstance(ids, (int, long)):
+            ids = [ids]
+
+        for punch in self.browse(cr, uid, ids, context=context):
+            if punch.state in ['verified', 'locked'] and (vals.get('name', False) or vals.get('action', False) or vals.get('employee_id', False)):
+                raise osv.except_osv(
+                    _('The record cannot be modified!'), _('You may not write to a record that is in a %s state:\nEmployee: %s, Date: %s, Action: %s') %
+                    (punch.state, punch.employee_id.name, punch.name, punch.action))
+
+        return super(hr_attendance, self).write(cr, uid, ids, vals, context=context)

=== added file 'hr_payroll_period/hr_attendance_workflow.xml'
--- hr_payroll_period/hr_attendance_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/hr_attendance_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Workflow Definition -->
+        <record id="wkf_attendance" model="workflow">
+            <field name="name">hr.attendance.basic</field>
+            <field name="osv">hr.attendance</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (Stages) -->
+        
+        <record id="act_draft" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_attendance"/>
+            <field name="name">draft</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'draft'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_verified" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_attendance"/>
+            <field name="name">verified</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'verified'})</field>
+        </record>
+        
+        <record id="act_locked" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_attendance"/>
+            <field name="name">locked</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'locked'})</field>
+        </record>
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="draft2locked" model="workflow.transition">
+            <field name="act_from" ref="act_draft"/>
+            <field name="act_to" ref="act_locked"/>
+            <field name="signal">signal_lock</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="locked2draft" model="workflow.transition">
+            <field name="act_from" ref="act_locked"/>
+            <field name="act_to" ref="act_draft"/>
+            <field name="signal">signal_unlock</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_payroll_period/hr_payroll_period.py'
--- hr_payroll_period/hr_payroll_period.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/hr_payroll_period.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,808 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2011,2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import calendar
+import netsvc
+import time
+from datetime import date, datetime, timedelta
+from dateutil.relativedelta import relativedelta
+from openerp.tools.safe_eval import safe_eval as eval
+from openerp.tools.translate import _
+from osv import fields, osv
+from pytz import common_timezones, timezone, utc
+from tools.translate import _
+import logging
+_logger = logging.getLogger(__name__)
+
+# Obtained from: http://stackoverflow.com/questions/4130922/how-to-increment-datetime-month-in-python
+#
+
+
+def add_months(sourcedate, months):
+    month = sourcedate.month - 1 + months
+    year = sourcedate.year + month / 12
+    month = month % 12 + 1
+    day = min(sourcedate.day, calendar.monthrange(year, month)[1])
+    return datetime(year, month, day)
+
+
+class hr_payroll_period(osv.osv):
+
+    _name = 'hr.payroll.period'
+
+    _inherit = ['mail.thread', 'ir.needaction_mixin']
+
+    _columns = {
+        'name': fields.char('Description', size=256, required=True),
+        'schedule_id': fields.many2one('hr.payroll.period.schedule', 'Payroll Period Schedule',
+                                       required=True),
+        'date_start': fields.datetime('Start Date', required=True),
+        'date_end': fields.datetime('End Date', required=True),
+        'register_id': fields.many2one('hr.payroll.register', 'Payroll Register', readonly=True,
+                                       states={
+                                           'generate': [('readonly', False)]}),
+        'state': fields.selection([('open', 'Open'),
+                                   ('ended', 'End of Period Processing'),
+                                   ('locked', 'Locked'),
+                                   ('generate', 'Generating Payslips'),
+                                   ('payment', 'Payment'),
+                                   ('closed', 'Closed')],
+                                  'State', select=True, readonly=True),
+    }
+
+    _order = "date_start, name desc"
+
+    _defaults = {
+        'state': 'open',
+    }
+
+    _track = {
+        'state': {
+            'hr_payroll_period.mt_state_open': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open',
+            'hr_payroll_period.mt_state_end': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'ended',
+            'hr_payroll_period.mt_state_lock': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'locked',
+            'hr_payroll_period.mt_state_generate': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'generate',
+            'hr_payroll_period.mt_state_payment': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'payment',
+            'hr_payroll_period.mt_state_close': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'closed',
+        },
+    }
+
+    def _needaction_domain_get(self, cr, uid, context=None):
+
+        users_obj = self.pool.get('res.users')
+        domain = []
+
+        if users_obj.has_group(cr, uid, 'hr_security.group_payroll_manager'):
+            domain = [('state', 'not in', ['open', 'closed'])]
+            return domain
+
+        return False
+
+    def is_ended(self, cr, uid, period_id, context=None):
+
+        #
+        # XXX - Someone who cares about DST should update this code to handle it.
+        #
+
+        flag = False
+        if period_id:
+            utc_tz = timezone('UTC')
+            utcDtNow = utc_tz.localize(datetime.now(), is_dst=False)
+            period = self.browse(cr, uid, period_id, context=context)
+            if period:
+                dtEnd = datetime.strptime(period.date_end, '%Y-%m-%d %H:%M:%S')
+                utcDtEnd = utc_tz.localize(dtEnd, is_dst=False)
+                if utcDtNow > utcDtEnd + timedelta(minutes=(period.schedule_id.ot_max_rollover_hours * 60)):
+                    flag = True
+        return flag
+
+    def try_signal_end_period(self, cr, uid, context=None):
+        """Method called, usually by cron, to transition any payroll periods
+        that are past their end date.
+        """
+
+        #
+        # XXX - Someone who cares about DST should update this code to handle it.
+        #
+
+        utc_tz = timezone('UTC')
+        utcDtNow = utc_tz.localize(datetime.now(), is_dst=False)
+        period_ids = self.search(cr, uid, [
+            ('state', 'in', ['open']),
+            ('date_end', '<=', utcDtNow.strftime(
+                '%Y-%m-%d %H:%M:%S')),
+        ], context=context)
+        if len(period_ids) == 0:
+            return
+
+        wf_service = netsvc.LocalService('workflow')
+        for pid in period_ids:
+            wf_service.trg_validate(
+                uid, 'hr.payroll.period', pid, 'end_period', cr)
+
+    def set_state_ended(self, cr, uid, ids, context=None):
+
+        #
+        # XXX - Someone who cares about DST should update this code to handle it.
+        #
+
+        wf_service = netsvc.LocalService('workflow')
+        attendance_obj = self.pool.get('hr.attendance')
+        detail_obj = self.pool.get('hr.schedule.detail')
+        holiday_obj = self.pool.get('hr.holidays')
+        for period in self.browse(cr, uid, ids, context=context):
+            utc_tz = timezone('UTC')
+            dt = datetime.strptime(period.date_start, '%Y-%m-%d %H:%M:%S')
+            utcDtStart = utc_tz.localize(dt, is_dst=False)
+            dt = datetime.strptime(period.date_end, '%Y-%m-%d %H:%M:%S')
+            utcDtEnd = utc_tz.localize(dt, is_dst=False)
+            if period.state in ['locked', 'generate']:
+                for contract in period.schedule_id.contract_ids:
+                    employee = contract.employee_id
+
+                    # Unlock attendance
+                    punch_ids = attendance_obj.search(cr, uid, [
+                        ('employee_id', '=', employee.id),
+                        '&', (
+                            'name', '>=', utcDtStart.strftime('%Y-%m-%d %H:%M:%S')),
+                        ('name', '<=', utcDtEnd.strftime(
+                            '%Y-%m-%d %H:%M:%S')),
+                    ], order='name', context=context)
+                    [wf_service.trg_validate(uid, 'hr.attendance', pid, 'signal_unlock', cr)
+                     for pid in punch_ids]
+
+                    # Unlock schedules
+                    detail_ids = detail_obj.search(cr, uid, [
+                        ('schedule_id.employee_id', '=', employee.id),
+                        '&', (
+                            'date_start', '>=', utcDtStart.strftime('%Y-%m-%d %H:%M:%S')),
+                        ('date_start', '<=', utcDtEnd.strftime(
+                            '%Y-%m-%d %H:%M:%S')),
+                    ], order='date_start', context=context)
+                    [wf_service.trg_validate(uid, 'hr.schedule.detail', did, 'signal_unlock', cr)
+                     for did in detail_ids]
+
+                    # Unlock holidays/leaves that end in the current period
+                    holiday_ids = holiday_obj.search(cr, uid, [
+                        ('employee_id', '=', employee.id),
+                        '&', (
+                            'date_to', '>=', utcDtStart.strftime('%Y-%m-%d %H:%M:%S')),
+                        ('date_to', '<=', utcDtEnd.strftime(
+                            '%Y-%m-%d %H:%M:%S')),
+                    ],
+                        context=context)
+                    for hid in holiday_ids:
+                        holiday_obj.write(
+                            cr, uid, [hid], {
+                                'payroll_period_state': 'unlocked'},
+                            context=context)
+
+            self.write(cr, uid, period.id, {'state': 'ended'}, context=context)
+
+        return True
+
+    def set_state_locked(self, cr, uid, ids, context=None):
+
+        #
+        # XXX - Someone who cares about DST should update this code to handle it.
+        #
+
+        wkf_service = netsvc.LocalService('workflow')
+        attendance_obj = self.pool.get('hr.attendance')
+        detail_obj = self.pool.get('hr.schedule.detail')
+        holiday_obj = self.pool.get('hr.holidays')
+        for period in self.browse(cr, uid, ids, context=context):
+            utc_tz = timezone('UTC')
+            dt = datetime.strptime(period.date_start, '%Y-%m-%d %H:%M:%S')
+            utcDtStart = utc_tz.localize(dt, is_dst=False)
+            dt = datetime.strptime(period.date_end, '%Y-%m-%d %H:%M:%S')
+            utcDtEnd = utc_tz.localize(dt, is_dst=False)
+            for contract in period.schedule_id.contract_ids:
+                employee = contract.employee_id
+
+                # Lock sign-in and sign-out attendance records
+                punch_ids = attendance_obj.search(cr, uid, [
+                    ('employee_id', '=', employee.id),
+                    '&', (
+                        'name', '>=', utcDtStart.strftime('%Y-%m-%d %H:%M:%S')),
+                    ('name', '<=', utcDtEnd.strftime(
+                        '%Y-%m-%d %H:%M:%S')),
+                ], order='name', context=context)
+                for pid in punch_ids:
+                    wkf_service.trg_validate(
+                        uid, 'hr.attendance', pid, 'signal_lock', cr)
+
+                # Lock schedules
+                detail_ids = detail_obj.search(cr, uid, [
+                    ('schedule_id.employee_id', '=', employee.id),
+                    '&', (
+                        'date_start', '>=', utcDtStart.strftime('%Y-%m-%d %H:%M:%S')),
+                    ('date_start', '<=', utcDtEnd.strftime(
+                        '%Y-%m-%d %H:%M:%S')),
+                ], order='date_start', context=context)
+                for did in detail_ids:
+                    wkf_service.trg_validate(
+                        uid, 'hr.schedule.detail', did, 'signal_lock', cr)
+
+                # Lock holidays/leaves that end in the current period
+                holiday_ids = holiday_obj.search(cr, uid, [
+                    ('employee_id', '=', employee.id),
+                    '&', (
+                        'date_to', '>=', utcDtStart.strftime('%Y-%m-%d %H:%M:%S')),
+                    ('date_to', '<=', utcDtEnd.strftime(
+                        '%Y-%m-%d %H:%M:%S')),
+                ],
+                    context=context)
+                for hid in holiday_ids:
+                    holiday_obj.write(
+                        cr, uid, [hid], {'payroll_period_state': 'locked'},
+                        context=context)
+
+            self.write(cr, uid, period.id, {
+                       'state': 'locked'}, context=context)
+
+        return True
+
+    def set_state_closed(self, cr, uid, ids, context=None):
+
+        return self.write(cr, uid, ids, {'state': 'closed'}, context=context)
+
+
+class hr_payperiod_schedule(osv.osv):
+
+    _name = 'hr.payroll.period.schedule'
+
+    def _tz_list(self, cr, uid, context=None):
+
+        res = tuple()
+        for name in common_timezones:
+            res += ((name, name),)
+        return res
+
+    _columns = {
+        'name': fields.char('Description', size=256, required=True),
+        'tz': fields.selection(_tz_list, 'Time Zone', required=True),
+        'paydate_biz_day': fields.boolean('Pay Date on a Business Day'),
+        'ot_week_startday': fields.selection([
+            ('0', _('Sunday')),
+            ('1', _('Monday')),
+            ('2', _('Tuesday')),
+            ('3', _('Wednesday')),
+            ('4', _('Thursday')),
+            ('5', _('Friday')),
+            ('6', _('Saturday')),
+        ],
+            'Start of Week', required=True),
+        'ot_max_rollover_hours': fields.integer('OT Max. Continous Hours', required=True),
+        'ot_max_rollover_gap': fields.integer('OT Max. Continuous Hours Gap (in Min.)', required=True),
+        'type': fields.selection([
+            ('manual', 'Manual'),
+            ('monthly', 'Monthly'),
+        ],
+            'Type', required=True),
+        'mo_firstday': fields.selection([
+            ('1', '1'), ('2', '2'), ('3', '3'), (
+                '4', '4'), ('5', '5'), ('6', '6'), ('7', '7'),
+            ('8', '8'), ('9', '9'), ('10', '10'), ('11', '11'), (
+                '12', '12'), ('13', '13'), ('14', '14'),
+            ('15', '15'), ('16', '16'), ('17', '17'), (
+                '18', '18'), ('19', '19'), ('20', '20'), ('21', '21'),
+            ('22', '22'), ('23', '23'), ('24', '24'), (
+                '25', '25'), ('26', '26'), ('27', '27'), ('28', '28'),
+            ('29', '29'), (
+                '30', '30'), ('31', '31'),
+        ],
+            'Start Day'),
+        'mo_paydate': fields.selection([
+            ('1', '1'), ('2', '2'), ('3', '3'), (
+                '4', '4'), ('5', '5'), ('6', '6'), ('7', '7'),
+            ('8', '8'), ('9', '9'), ('10', '10'), ('11', '11'), (
+                '12', '12'), ('13', '13'), ('14', '14'),
+            ('15', '15'), ('16', '16'), ('17', '17'), (
+                '18', '18'), ('19', '19'), ('20', '20'), ('21', '21'),
+            ('22', '22'), ('23', '23'), ('24', '24'), (
+                '25', '25'), ('26', '26'), ('27', '27'), ('28', '28'),
+            ('29', '29'), (
+                '30', '30'), ('31', '31'),
+        ],
+            'Pay Date'),
+        'contract_ids': fields.one2many('hr.contract', 'pps_id', 'Contracts'),
+        'pay_period_ids': fields.one2many('hr.payroll.period', 'schedule_id', 'Pay Periods'),
+        'initial_period_date': fields.date('Initial Period Start Date'),
+        'active': fields.boolean('Active'),
+    }
+
+    _defaults = {
+        'ot_week_startday': '1',
+        'ot_max_rollover_hours': 6,
+        'ot_max_rollover_gap': 60,
+        'mo_firstday': '1',
+        'mo_paydate': '3',
+        'type': 'monthly',
+        'active': True,
+    }
+
+    def _check_initial_date(self, cr, uid, ids, context=None):
+
+        for obj in self.browse(cr, uid, ids, context=context):
+            if obj.type in ['monthly'] and not obj.initial_period_date:
+                return False
+
+        return True
+
+    _constraints = [
+        (_check_initial_date,
+         'You must supply an Initial Period Start Date', ['type']),
+    ]
+
+    def add_pay_period(self, cr, uid, ids, context=None):
+
+        def get_period_year(dt):
+
+            month_number = 0
+            year_number = 0
+            if dt.day < 15:
+                month_number = dt.month
+                year_number = dt.year
+            else:
+                dtTmp = add_months(dt, 1)
+                month_number = dtTmp.month
+                year_number = dtTmp.year
+            return month_number, year_number
+
+        #
+        # XXX - Someone who cares about DST should update this code to handle it.
+        #
+
+        schedule_obj = self.pool.get('hr.payroll.period.schedule')
+
+        data = None
+        latest = None
+        for sched in schedule_obj.browse(cr, uid, ids, context=context):
+            for p in sched.pay_period_ids:
+                if not latest:
+                    latest = p
+                    continue
+                if datetime.strptime(p.date_end, '%Y-%m-%d %H:%M:%S') > datetime.strptime(latest.date_end, '%Y-%m-%d %H:%M:%S'):
+                    latest = p
+            local_tz = timezone(sched.tz)
+            if not latest:
+                # No pay periods have been defined yet for this pay period
+                # schedule.
+                if sched.type == 'monthly':
+                    dtStart = datetime.strptime(
+                        sched.initial_period_date, '%Y-%m-%d')
+                    if dtStart.day > int(sched.mo_firstday):
+                        dtStart = add_months(dtStart, 1)
+                        dtStart = datetime(
+                            dtStart.year, dtStart.month, int(sched.mo_firstday), 0, 0, 0)
+                    elif dtStart.day < int(sched.mo_firstday):
+                        dtStart = datetime(
+                            dtStart.year, dtStart.month, int(sched.mo_firstday), 0, 0, 0)
+                    else:
+                        dtStart = datetime(
+                            dtStart.year, dtStart.month, dtStart.day, 0, 0, 0)
+                    dtEnd = add_months(dtStart, 1) - timedelta(days=1)
+                    dtEnd = datetime(
+                        dtEnd.year, dtEnd.month, dtEnd.day, 23, 59, 59)
+                    month_number, year_number = get_period_year(dtStart)
+
+                    # Convert from time zone of punches to UTC for storage
+                    utcStart = local_tz.localize(dtStart, is_dst=None)
+                    utcStart = utcStart.astimezone(utc)
+                    utcEnd = local_tz.localize(dtEnd, is_dst=None)
+                    utcEnd = utcEnd.astimezone(utc)
+
+                    data = {
+                        'name': 'Pay Period ' + str(month_number) + '/' + str(year_number),
+                        'schedule_id': sched.id,
+                        'date_start': utcStart.strftime('%Y-%m-%d %H:%M:%S'),
+                        'date_end': utcEnd.strftime('%Y-%m-%d %H:%M:%S'),
+                    }
+            else:
+                if sched.type == 'monthly':
+                    # Convert from UTC to timezone of punches
+                    utcStart = datetime.strptime(
+                        latest.date_end, '%Y-%m-%d %H:%M:%S')
+                    utc_tz = timezone('UTC')
+                    utcStart = utc_tz.localize(utcStart, is_dst=None)
+                    utcStart += timedelta(seconds=1)
+                    dtStart = utcStart.astimezone(local_tz)
+
+                    # Roll forward to the next pay period start and end times
+                    dtEnd = add_months(dtStart, 1) - timedelta(days=1)
+                    dtEnd = datetime(
+                        dtEnd.year, dtEnd.month, dtEnd.day, 23, 59, 59)
+                    month_number, year_number = get_period_year(dtStart)
+
+                    # Convert from time zone of punches to UTC for storage
+                    utcStart = dtStart.astimezone(utc_tz)
+                    utcEnd = local_tz.localize(dtEnd, is_dst=None)
+                    utcEnd = utcEnd.astimezone(utc)
+
+                    data = {
+                        'name': 'Pay Period ' + str(month_number) + '/' + str(year_number),
+                        'schedule_id': sched.id,
+                        'date_start': utcStart.strftime('%Y-%m-%d %H:%M:%S'),
+                        'date_end': utcEnd.strftime('%Y-%m-%d %H:%M:%S'),
+                    }
+            if data != None:
+                schedule_obj.write(cr, uid, sched.id, {
+                                   'pay_period_ids': [(0, 0, data)]}, context=context)
+
+    def _get_latest_period(self, cr, uid, sched_id, context=None):
+
+        sched = self.browse(cr, uid, sched_id, context=context)
+        latest_period = False
+        for period in sched.pay_period_ids:
+            if not latest_period:
+                latest_period = period
+                continue
+            if datetime.strptime(period.date_end, '%Y-%m-%d %H:%M:%S') > datetime.strptime(latest_period.date_end, '%Y-%m-%d %H:%M:%S'):
+                latest_period = period
+
+        return latest_period
+
+    def try_create_new_period(self, cr, uid, context=None):
+        '''Try and create pay periods for up to 3 months from now.'''
+
+        #
+        # XXX - Someone who cares about DST should update this code to handle it.
+        #
+
+        dtNow = datetime.now()
+        utc_tz = timezone('UTC')
+        sched_obj = self.pool.get('hr.payroll.period.schedule')
+        sched_ids = sched_obj.search(cr, uid, [], context=context)
+        for sched in sched_obj.browse(cr, uid, sched_ids, context=context):
+            if sched.type == 'monthly':
+                firstday = sched.mo_firstday
+            else:
+                continue
+            dtNow = datetime.strptime(
+                dtNow.strftime('%Y-%m-' + firstday + ' 00:00:00'), '%Y-%m-%d %H:%M:%S')
+            loclDTNow = timezone(sched.tz).localize(dtNow, is_dst=False)
+            utcDTFuture = loclDTNow.astimezone(
+                utc_tz) + relativedelta(months=+3)
+
+            if not sched.pay_period_ids:
+                self.add_pay_period(cr, uid, [sched.id], context=context)
+
+            latest_period = self._get_latest_period(
+                cr, uid, sched.id, context=context)
+            utcDTStart = utc_tz.localize(
+                datetime.strptime(latest_period.date_start, '%Y-%m-%d %H:%M:%S'), is_dst=False)
+            while utcDTFuture > utcDTStart:
+                self.add_pay_period(cr, uid, [sched.id], context=context)
+                latest_period = self._get_latest_period(
+                    cr, uid, sched.id, context=context)
+                utcDTStart = utc_tz.localize(
+                    datetime.strptime(latest_period.date_start, '%Y-%m-%d %H:%M:%S'), is_dst=False)
+
+
+class contract_init(osv.Model):
+
+    _inherit = 'hr.contract.init'
+
+    _columns = {
+        'pay_sched_id': fields.many2one('hr.payroll.period.schedule', 'Payroll Period Schedule',
+                                        readonly=True, states={
+                                            'draft': [('readonly', False)]}),
+    }
+
+
+class hr_contract(osv.osv):
+
+    _name = 'hr.contract'
+    _inherit = 'hr.contract'
+
+    _columns = {
+        'pps_id': fields.many2one('hr.payroll.period.schedule', 'Payroll Period Schedule', required=True),
+    }
+
+    def _get_pay_sched(self, cr, uid, context=None):
+
+        res = False
+        init = self.get_latest_initial_values(cr, uid, context=context)
+        if init != None and init.pay_sched_id:
+            res = init.pay_sched_id.id
+        return res
+
+    _defaults = {
+        'pps_id': _get_pay_sched,
+    }
+
+
+class hr_payslip(osv.osv):
+
+    _name = 'hr.payslip'
+    _inherit = 'hr.payslip'
+
+    _columns = {
+        'exception_ids': fields.one2many('hr.payslip.exception', 'slip_id',
+                                         'Exceptions', readonly=True),
+    }
+
+    def compute_sheet(self, cr, uid, ids, context=None):
+
+        super(hr_payslip, self).compute_sheet(cr, uid, ids, context=context)
+
+        class BrowsableObject(object):
+
+            def __init__(self, pool, cr, uid, employee_id, dict):
+                self.pool = pool
+                self.cr = cr
+                self.uid = uid
+                self.employee_id = employee_id
+                self.dict = dict
+
+            def __getattr__(self, attr):
+                return attr in self.dict and self.dict.__getitem__(attr) or 0.0
+
+        class InputLine(BrowsableObject):
+
+            """a class that will be used into the python code, mainly for usability purposes"""
+
+            def sum(self, code, from_date, to_date=None):
+                if to_date is None:
+                    to_date = datetime.now().strftime('%Y-%m-%d')
+                result = 0.0
+                self.cr.execute("SELECT sum(amount) as sum\
+                            FROM hr_payslip as hp, hr_payslip_input as pi \
+                            WHERE hp.employee_id = %s AND hp.state = 'done' \
+                            AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s",
+                                (self.employee_id, from_date, to_date, code))
+                res = self.cr.fetchone()[0]
+                return res or 0.0
+
+        class WorkedDays(BrowsableObject):
+
+            """a class that will be used into the python code, mainly for usability purposes"""
+
+            def _sum(self, code, from_date, to_date=None):
+                if to_date is None:
+                    to_date = datetime.now().strftime('%Y-%m-%d')
+                result = 0.0
+                self.cr.execute("SELECT sum(number_of_days) as number_of_days, sum(number_of_hours) as number_of_hours\
+                            FROM hr_payslip as hp, hr_payslip_worked_days as pi \
+                            WHERE hp.employee_id = %s AND hp.state = 'done'\
+                            AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pi.payslip_id AND pi.code = %s",
+                                (self.employee_id, from_date, to_date, code))
+                return self.cr.fetchone()
+
+            def sum(self, code, from_date, to_date=None):
+                res = self._sum(code, from_date, to_date)
+                return res and res[0] or 0.0
+
+            def sum_hours(self, code, from_date, to_date=None):
+                res = self._sum(code, from_date, to_date)
+                return res and res[1] or 0.0
+
+        class Payslips(BrowsableObject):
+
+            """a class that will be used into the python code, mainly for usability purposes"""
+
+            def sum(self, code, from_date, to_date=None):
+                if to_date is None:
+                    to_date = datetime.now().strftime('%Y-%m-%d')
+                self.cr.execute("SELECT sum(case when hp.credit_note = False then (pl.total) else (-pl.total) end)\
+                            FROM hr_payslip as hp, hr_payslip_line as pl \
+                            WHERE hp.employee_id = %s AND hp.state = 'done' \
+                            AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s",
+                                (self.employee_id, from_date, to_date, code))
+                res = self.cr.fetchone()
+                return res and res[0] or 0.0
+
+        rule_obj = self.pool.get('hr.payslip.exception.rule')
+        rule_ids = rule_obj.search(
+            cr, uid, [('active', '=', True)], context=context)
+        rule_seq = []
+        for i in rule_ids:
+            data = rule_obj.read(cr, uid, i, ['sequence'], context=context)
+            rule_seq.append((i, data['sequence']))
+        sorted_rule_ids = [
+            id for id, sequence in sorted(rule_seq, key=lambda x:x[1])]
+
+        for payslip in self.browse(cr, uid, ids, context=context):
+            payslip_obj = Payslips(
+                self.pool, cr, uid, payslip.employee_id.id, payslip)
+
+            codes = []
+            categories = {}
+            for line in payslip.details_by_salary_rule_category:
+                if line.code not in codes:
+                    categories[line.code] = line
+                    codes.append(line.code)
+            categories_obj = BrowsableObject(
+                self.pool, cr, uid, payslip.employee_id.id, categories)
+
+            worked_days = {}
+            for line in payslip.worked_days_line_ids:
+                worked_days[line.code] = line
+            worked_days_obj = WorkedDays(
+                self.pool, cr, uid, payslip.employee_id.id, worked_days)
+
+            inputs = {}
+            for line in payslip.input_line_ids:
+                inputs[line.code] = line
+            input_obj = InputLine(
+                self.pool, cr, uid, payslip.employee_id.id, inputs)
+
+            temp_dict = {}
+            utils_dict = self.get_utilities_dict(
+                cr, uid, payslip.contract_id, payslip, context=context)
+            for k, v in utils_dict.iteritems():
+                k_obj = BrowsableObject(
+                    self.pool, cr, uid, payslip.employee_id.id, v)
+                temp_dict.update({k: k_obj})
+            utils_obj = BrowsableObject(
+                self.pool, cr, uid, payslip.employee_id.id, temp_dict)
+
+            localdict = {'categories': categories_obj,
+                         'payslip': payslip_obj,
+                         'worked_days': worked_days_obj,
+                         'inputs': input_obj,
+                         'utils': utils_obj}
+            localdict['result'] = None
+
+            for rule in rule_obj.browse(cr, uid, sorted_rule_ids, context=context):
+                if rule_obj.satisfy_condition(cr, uid, rule.id, localdict, context=context):
+                    val = {
+                        'name': rule.name,
+                        'slip_id': payslip.id,
+                        'rule_id': rule.id,
+                    }
+                    self.pool.get('hr.payslip.exception').create(
+                        cr, uid, val, context=context)
+
+        return True
+
+
+class hr_payslip_exception(osv.osv):
+
+    _name = 'hr.payslip.exception'
+    _description = 'Payroll Exception'
+
+    _columns = {
+        'name': fields.char('Name', size=256, required=True, readonly=True),
+        'rule_id': fields.many2one('hr.payslip.exception.rule', 'Rule', ondelete='cascade', readonly=True),
+        'slip_id': fields.many2one('hr.payslip', 'Pay Slip', ondelete='cascade', readonly=True),
+        'severity': fields.related('rule_id', 'severity', type="char", string="Severity", store=True, readonly=True),
+    }
+
+# This is almost 100% lifted from hr_payroll/hr.salary.rule
+# I ommitted the parts I don't use.
+#
+
+
+class hr_payslip_exception_rule(osv.osv):
+
+    _name = 'hr.payslip.exception.rule'
+    _description = 'Rules describing pay slips in an abnormal state'
+
+    _columns = {
+        'name': fields.char('Name', size=256, required=True),
+        'code': fields.char('Code', size=64, required=True),
+        'sequence': fields.integer('Sequence', required=True, help='Use to arrange calculation sequence', select=True),
+        'active': fields.boolean('Active', help="If the active field is set to false, it will allow you to hide the rule without removing it."),
+        'company_id': fields.many2one('res.company', 'Company'),
+        'condition_select': fields.selection([('none', 'Always True'), ('python', 'Python Expression')], "Condition Based on", required=True),
+        'condition_python': fields.text('Python Condition', readonly=False, help='The condition that triggers the exception.'),
+        'severity': fields.selection((
+            ('low', 'Low'),
+            ('medium', 'Medium'),
+            ('high', 'High'),
+            ('critical', 'Critical'),
+        ), 'Severity', required=True),
+        'note': fields.text('Description'),
+    }
+
+    _defaults = {
+        'active': True,
+        'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'hr.payslip.exception.rule', context=context),
+        'sequence': 5,
+        'severity': 'low',
+        'condition_select': 'none',
+        'condition_python':
+'''
+# Available variables:
+#----------------------
+# payslip: object containing the payslips
+# contract: hr.contract object
+# categories: object containing the computed salary rule categories (sum of amount of all rules belonging to that category).
+# worked_days: object containing the computed worked days
+# inputs: object containing the computed inputs
+
+# Note: returned value have to be set in the variable 'result'
+
+result = categories.GROSS.amount > categories.NET.amount''',
+    }
+
+    def satisfy_condition(self, cr, uid, rule_id, localdict, context=None):
+        """
+        @param rule_id: id of hr.payslip.exception.rule to be tested
+        @param contract_id: id of hr.contract to be tested
+        @return: returns True if the given rule match the condition for the given contract. Return False otherwise.
+        """
+        rule = self.browse(cr, uid, rule_id, context=context)
+
+        if rule.condition_select == 'none':
+            return True
+        else:  # python code
+            try:
+                eval(rule.condition_python,
+                     localdict, mode='exec', nocopy=True)
+                return 'result' in localdict and localdict['result'] or False
+            except:
+                raise osv.except_osv(
+                    _('Error!'), _('Wrong python condition defined for payroll exception rule %s (%s).') % (rule.name, rule.code))
+
+
+class hr_payslip_amendment(osv.osv):
+
+    _name = 'hr.payslip.amendment'
+    _inherit = 'hr.payslip.amendment'
+
+    _columns = {
+        'pay_period_id': fields.many2one('hr.payroll.period', 'Pay Period', domain=[('state', 'in', ['open', 'ended', 'locked', 'generate'])], required=False, readonly=True, states={'draft': [('readonly', False)], 'validate': [('required', True)], 'done': [('required', True)]}),
+    }
+
+
+class hr_holidays_status(osv.osv):
+
+    _name = 'hr.holidays.status'
+    _inherit = 'hr.holidays.status'
+
+    _columns = {
+        'code': fields.char('Code', size=16, required=True),
+    }
+
+    _sql_constraints = [
+        ('code_unique', 'UNIQUE(code)', 'Codes for leave types must be unique!')]
+
+
+class hr_holidays(osv.Model):
+
+    _name = 'hr.holidays'
+    _inherit = 'hr.holidays'
+
+    _columns = {
+        'payroll_period_state': fields.selection([('unlocked', 'Unlocked'), ('locked', 'Locked')],
+                                                 'Payroll Period State', readonly=True),
+    }
+
+    _defaults = {
+        'payroll_period_state': 'unlocked',
+    }
+
+    def unlink(self, cr, uid, ids, context=None):
+        for h in self.browse(cr, uid, ids, context=context):
+            if h.payroll_period_state == 'locked':
+                raise osv.except_osv(_('Warning!'),
+                                     _('You cannot delete a leave which belongs to a payroll period that has been locked.'))
+        return super(hr_holidays, self).unlink(cr, uid, ids, context)
+
+    def write(self, cr, uid, ids, vals, context=None):
+        for h in self.browse(cr, uid, ids, context=context):
+            if h.payroll_period_state == 'locked' and not vals.get('payroll_period_state', False):
+                raise osv.except_osv(_('Warning!'),
+                                     _('You cannot modify a leave which belongs to a payroll period that has been locked.'))
+        return super(hr_holidays, self).write(cr, uid, ids, vals, context=context)

=== added file 'hr_payroll_period/hr_payroll_period_cron.xml'
--- hr_payroll_period/hr_payroll_period_cron.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/hr_payroll_period_cron.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <record model="ir.cron" id="hr_payroll_period_ended_cron">
+            <field name="name">Payroll Periods Past End Date</field>
+            <field name="interval_number">1</field>
+            <field name="interval_type">hours</field>
+            <field name="numbercall">-1</field>
+            <field eval="(DateTime.now() + timedelta(minutes=60)).strftime('%Y-%m-%d %H:05:00')" name="nextcall"/>
+            <field eval="False" name="doall"/>
+            <field eval="'hr.payroll.period'" name="model"/>
+            <field eval="'try_signal_end_period'" name="function"/>
+            <field eval="'()'" name="args"/>
+        </record>
+        
+        <record model="ir.cron" id="hr_payroll_period_create_cron">
+            <field name="name">Create New Payroll Periods</field>
+            <field name="interval_number">1</field>
+            <field name="interval_type">hours</field>
+            <field name="numbercall">-1</field>
+            <field eval="(DateTime.now() + timedelta(minutes=60)).strftime('%Y-%m-%d %H:05:00')" name="nextcall"/>
+            <field eval="False" name="doall"/>
+            <field eval="'hr.payroll.period.schedule'" name="model"/>
+            <field eval="'try_create_new_period'" name="function"/>
+            <field eval="'()'" name="args"/>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_payroll_period/hr_payroll_period_report.xml'
--- hr_payroll_period/hr_payroll_period_report.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/hr_payroll_period_report.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<openerp>
+    <data>
+
+        <report
+            auto="False"
+            id="payslip_report"
+            model="hr.payslip"
+            name="payslip"
+            rml="hr_payroll/report/report_payslip.rml"
+            string="Employee PaySlip" />
+
+        <report
+            auto="False"
+            id="payslip_details_report"
+            model="hr.payslip"
+            name="paylip.details"
+            rml="hr_payroll/report/report_payslip_details.rml"
+            string="PaySlip Details" />
+
+        <report
+            auto="False"
+            menu="False"
+            id="contribution_register"
+            model="hr.contribution.register"
+            name="contribution.register.lines"
+            rml="hr_payroll/report/report_contribution_register.rml"
+            string="PaySlip Lines By Conribution Register" />
+
+    </data>
+</openerp>

=== added file 'hr_payroll_period/hr_payroll_period_view.xml'
--- hr_payroll_period/hr_payroll_period_view.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/hr_payroll_period_view.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Submenu for advanced views -->
+        <menuitem name="Advanced"
+                  id="menu_payroll_advanced"
+                  parent="hr_payroll.menu_hr_root_payroll"
+                  sequence="100" groups="base.group_hr_manager,hr_security.group_payroll_manager"/>
+        
+        <!-- Move the following menu items from the payroll root menu to the advanced one -->
+        <menuitem action="hr_payroll.action_view_hr_payslip_form"
+            id="hr_payroll.menu_department_tree" parent="menu_payroll_advanced" groups="base.group_hr_manager,hr_security.group_payroll_manager" sequence="10"/>        
+        <menuitem action="hr_payroll.action_hr_payslip_run_tree"
+            id="hr_payroll.menu_hr_payslip_run" parent="menu_payroll_advanced" groups="base.group_hr_manager,hr_security.group_payroll_manager" sequence="20"/>
+        <!-- Move 'Payroll Register' from hr_payroll_register -->
+        <menuitem action="hr_payroll_register.open_payroll_registers"
+            id="hr_payroll_register.menu_payroll_register" parent="menu_payroll_advanced" groups="base.group_hr_manager,hr_security.group_payroll_manager" sequence="30"/>
+        
+        <!-- Payroll Period -->
+        
+        <record id="view_payroll_period_tree" model="ir.ui.view">
+            <field name="name">hr.payroll.period.tree</field>
+            <field name="model">hr.payroll.period</field>
+            <field name="arch" type="xml">
+                <tree string="Payroll Periods">
+                    <field name="name"/>
+                    <field name="date_start"/>
+                    <field name="date_end"/>
+                    <field name="state"/>
+                </tree>
+            </field>
+        </record>
+        <record id="view_payroll_period_form" model="ir.ui.view">
+            <field name="name">hr.payroll.period.form</field>
+            <field name="model">hr.payroll.period</field>
+            <field name="arch" type="xml">
+                <form string="Payroll Period" version="7.0">
+                    <header>
+                        <button name="%(action_payroll_period_end)d" type="action" states="open,ended,locked,generate,payment" class="oe_highlight" string="End of Pay Period Wizard"/>
+                        <field name="state" widget="statusbar"/>
+                    </header>
+                    <group>
+                        <group>
+                            <field name="schedule_id"/>
+                            <label for="date_start" string="Interval"/>
+                            <div>
+                                <field name="date_start" nolabel="1" class="oe_inline"/> - 
+                                <field name="date_end" nolabel="1" class="oe_inline"/>
+                            </div>
+                            <field name="name"/>
+                            <field name="register_id"/>
+                        </group>
+                    </group>
+                    <div class="oe_chatter">
+                        <field name="message_follower_ids" widget="mail_followers"/>
+                        <field name="message_ids" widget="mail_thread"/>
+                    </div>
+                </form>
+            </field>
+        </record>
+        <record id="open_payroll_period_view" model="ir.actions.act_window">
+            <field name="name">Payroll Periods</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="open_payroll_period_view"
+                  id="menu_payroll_period_view"
+                  parent="hr_payroll.menu_hr_root_payroll"
+                  groups="hr_security.group_payroll_user,base.group_hr_manager"
+                  sequence="1"/>
+        
+        <!-- End of Pay Period -->
+        
+        <record id="action_pay_period_end" model="ir.actions.act_window">
+            <field name="name">End of Pay Period</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="res_model">hr.payroll.period</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+            <field name="domain">[('state','in',['ended','locked','generate','payment'])]</field>
+            <field name="help" type="html">
+              <p>
+                There are no current pay periods that are past their end date.
+              </p>
+            </field>
+        </record>
+        <menuitem action="action_pay_period_end"
+                  id="menu_pay_period_end"
+                  parent="hr_payroll.menu_hr_root_payroll"
+                  groups="hr_security.group_payroll_user,base.group_hr_manager"
+                  sequence="2"/>
+        
+        <!-- Payroll Period Schedule -->
+        
+        <record id="view_payperiod_schedule_tree" model="ir.ui.view">
+            <field name="name">hr.payroll.period.schedule.tree</field>
+            <field name="model">hr.payroll.period.schedule</field>
+            <field name="arch" type="xml">
+                <tree string="Pay Period Schedules">
+                    <field name="name"/>
+                    <field name="type"/>
+                    <field name="tz"/>
+                    <field name="ot_week_startday"/>
+                    <field name="ot_max_rollover_hours"/>
+                    <field name="ot_max_rollover_gap"/>
+                    <field name="active"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_payperiod_schedule_form" model="ir.ui.view">
+            <field name="name">hr.payroll.period.schedule.form</field>
+            <field name="model">hr.payroll.period.schedule</field>
+            <field name="arch" type="xml">
+                <form string="Pay Period Schedule" version="7.0">
+                    <group>
+                        <group>
+                            <field name="name"/>
+                            <field name="tz"/>
+                            <field name="ot_week_startday"/>
+                            <field name="ot_max_rollover_hours"/>
+                            <field name="ot_max_rollover_gap"/>
+                            <field name="paydate_biz_day"/>
+                        </group>
+                        <group>
+                            <field name="type"/>
+                            <field name="mo_firstday" attrs="{'invisible': [('type','!=','monthly')]}"/>
+                            <field name="mo_paydate" attrs="{'invisible': [('type','!=','monthly')]}"/>
+                            <newline/>
+                            <field name="initial_period_date" attrs="{'invisible': [('type','!=','monthly')], 'required': [('type','=','monthly')]}"/>
+                            <field name="active"/>
+                        </group>
+                    </group>
+                    <group string="Pay Periods" colspan="4" col="1">
+                        <button name="add_pay_period" type="object" string="Add"/>
+                        <field name="pay_period_ids" nolabel="1"/>
+                    </group>
+                    <group string="Contracts" colspan="4" col="1">
+                        <field name="contract_ids" nolabel="1"/>
+                    </group>
+                </form>
+            </field>
+        </record>
+        <record id="open_payroll_period_schedule_view" model="ir.actions.act_window">
+            <field name="name">Payroll Period Schedules</field>
+            <field name="res_model">hr.payroll.period.schedule</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="open_payroll_period_schedule_view"
+                  id="menu_payroll_period_schedule_view"
+                  parent="hr_payroll.payroll_configure"
+                  groups="hr_security.group_payroll_user,base.group_hr_manager"
+                  sequence="20"/>
+        
+        <!-- Payroll Exception -->
+        
+        <record id="view_payroll_exception_tree" model="ir.ui.view">
+            <field name="name">hr.payslip.exception.tree</field>
+            <field name="model">hr.payslip.exception</field>
+            <field name="arch" type="xml">
+                <tree string="Payroll Exceptions" colors="red:severity == 'critical';orange:severity == 'high';blue:severity == 'medium';black:severity == 'low';">
+                    <field name="name"/>
+                    <field name="slip_id"/>
+                    <field name="severity"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_payroll_exception_form" model="ir.ui.view">
+            <field name="name">hr.payslip.exception.form</field>
+            <field name="model">hr.payslip.exception</field>
+            <field name="arch" type="xml">
+                <form string="Payroll Exception" version="7.0">
+                    <div class="oe_title">
+                        <label for="name" class="oe_edit_only"/>
+                        <h1><field name="name"/></h1>
+                    </div>
+                    <group>
+                        <group>
+                            <field name="rule_id"/>
+                            <field name="slip_id"/>
+                            <field name="severity"/>
+                        </group>
+                    </group>
+                </form>
+            </field>
+        </record>
+        <record id="open_payroll_exception_view" model="ir.actions.act_window">
+            <field name="name">Exceptions</field>
+            <field name="res_model">hr.payslip.exception</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="open_payroll_exception_view"
+                  id="menu_payroll_exception_view"
+                  parent="menu_payroll_advanced"
+                  groups="hr_security.group_payroll_user,base.group_hr_manager"
+                  sequence="40"/>
+        
+        <!-- Payroll Exception Rule -->
+        
+        <record id="view_exception_rule_tree" model="ir.ui.view">
+            <field name="name">hr.payslip.exception.rule.tree</field>
+            <field name="model">hr.payslip.exception.rule</field>
+            <field name="arch" type="xml">
+                <tree string="Payroll Exception Rules">
+                    <field name="name"/>
+                    <field name="code"/>
+                    <field name="severity"/>
+                    <field name="sequence"/>
+                </tree>
+            </field>
+        </record>
+        
+        <record id="view_exception_rule_form" model="ir.ui.view">
+            <field name="name">hr.payslip.exception.rule.form</field>
+            <field name="model">hr.payslip.exception.rule</field>
+            <field name="arch" type="xml">
+                <form string="Payroll Exception Rule" version="7.0">
+                    <div class="oe_title">
+                        <label for="name" class="oe_edit_only"/>
+                        <h1><field name="name"/></h1>
+                        <label for="code" class="oe_edit_only" string="Code"/>
+                        <h2>
+                            <field name="code"/>
+                        </h2>
+                    </div>
+                    <group>
+                        <group>
+                            <field name="severity"/>
+                            <field name="condition_select"/>
+                        </group>
+                        <group>
+                            <field name="active"/>
+                            <field name="sequence"/>
+                            <field name="company_id" widget="selection" groups="base.group_multi_company"/>
+                        </group>
+                    </group>
+                    <group string="Conditions" colspan="4">
+                        <field name="condition_python" nolabel="1" colspan="4" attrs="{'invisible':[('condition_select','&lt;&gt;','python')], 'required': [('condition_select','=','python')]}"/>
+                    </group>
+                    <group string="Note" colspan="4">
+                        <field name="note" nolabel="1"/>
+                    </group>
+                </form>
+            </field>
+        </record>
+        <record id="open_payroll_exception_rule_view" model="ir.actions.act_window">
+            <field name="name">Payroll Exception Rules</field>
+            <field name="res_model">hr.payslip.exception.rule</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree,form</field>
+        </record>
+        <menuitem action="open_payroll_exception_rule_view"
+                  id="menu_payroll_exception_rule_view"
+                  parent="hr_payroll.payroll_configure"
+                  groups="hr_security.group_payroll_user,base.group_hr_manager"
+                  sequence="30"/>
+        
+        <!-- Employment Contracts -->
+        
+        <record id="view_hr_contract_form_inherit" model="ir.ui.view">
+            <field name="name">hr.contract.form.inherit</field>
+            <field name="model">hr.contract</field>
+            <field name="inherit_id" ref="hr_payroll.hr_contract_form_inherit"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='struct_id']" position="after">
+                        <field name="pps_id"/>
+                    </xpath>
+                    <xpath expr="//field[@name='schedule_pay']" position="replace"/>
+                </data>
+            </field>
+        </record>
+        
+        <!-- Attendance Records -->
+        
+        <record id="view_hr_attendance_tree" model="ir.ui.view">
+            <field name="name">hr.attendance.tree.inherit</field>
+            <field name="model">hr.attendance</field>
+            <field name="inherit_id" ref="hr_attendance.view_attendance_tree"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//field[@name='action_desc']" position="after">
+                        <field name="state"/>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+        <record id="view_hr_attendance_form" model="ir.ui.view">
+            <field name="name">hr.attendance.form.inherit</field>
+            <field name="model">hr.attendance</field>
+            <field name="inherit_id" ref="hr_attendance.view_attendance_form"/>
+            <field name="arch" type="xml">
+                <data>
+                    <xpath expr="//form/sheet" position="before">
+                        <header>
+                            <field name="state" widget="statusbar"/>
+                        </header>
+                    </xpath>
+                </data>
+            </field>
+        </record>
+        
+        <!-- Pay Slip Amendment -->
+        
+        <record id="view_hr_payslip_amendment_tree" model="ir.ui.view">
+            <field name="name">hr.payslip.amendment.tree.inherit</field>
+            <field name="model">hr.payslip.amendment</field>
+            <field name="inherit_id" ref="hr_payslip_amendment.view_payslip_amendment_tree"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='employee_id']" position="after">
+                    <field name="pay_period_id"/>
+                </xpath>
+            </field>
+        </record>
+        <record id="view_hr_payslip_amendment_form" model="ir.ui.view">
+            <field name="name">hr.payslip.amendment.form.inherit</field>
+            <field name="model">hr.payslip.amendment</field>
+            <field name="inherit_id" ref="hr_payslip_amendment.view_payslip_amendment_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='name']" position="after">
+                    <field name="pay_period_id" widget="selection"/>
+                </xpath>
+            </field>
+        </record>
+        
+        <!-- Leave Types -->
+        
+        <record id="view_hr_holidays_status_form" model="ir.ui.view">
+            <field name="name">hr.holidays.status.form.inherit</field>
+            <field name="model">hr.holidays.status</field>
+            <field name="inherit_id" ref="hr_holidays.edit_holiday_status_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='name']" position="after">
+                    <field name="code"/>
+                </xpath>
+            </field>
+        </record>
+        
+        <!-- Pay Slips -->
+        <record id="view_payslip_form" model="ir.ui.view">
+            <field name="name">hr.payslip.form.exception</field>
+            <field name="model">hr.payslip</field>
+            <field name="inherit_id" ref="hr_payroll.view_hr_payslip_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//group[@name='accounting']" position="after">
+                    <group name="exceptions" string="Payroll Exceptions" colspan="4" col="2">
+                        <field name="exception_ids" nolabel="1">
+                            <tree string="Payroll Exceptions" colors="red:severity == 'critical';orange:severity == 'high';blue:severity == 'medium';black:severity == 'low';">
+                                <field name="name"/>
+                                <field name="severity"/>
+                            </tree>
+                        </field>
+                    </group>
+                </xpath>
+            </field>
+        </record>
+        
+        <!-- Initial Contract Settings -->
+        
+        <record id="view_contract_init_tree" model="ir.ui.view">
+            <field name="name">hr.contract.init.tree.payroll_period</field>
+            <field name="model">hr.contract.init</field>
+            <field name="inherit_id" ref="hr_contract_init.view_contract_init_tree"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='struct_id']" position="after">
+                    <field name="pay_sched_id"/>
+                </xpath>
+            </field>
+        </record>
+        
+        <record id="view_contract_init_form" model="ir.ui.view">
+            <field name="name">hr.contract.init.form.payroll_period</field>
+            <field name="model">hr.contract.init</field>
+            <field name="inherit_id" ref="hr_contract_init.view_contract_init_form"/>
+            <field name="arch" type="xml">
+                <xpath expr="//field[@name='struct_id']" position="after">
+                    <field name="pay_sched_id"/>
+                </xpath>
+            </field>
+        </record>
+        
+    </data>
+</openerp>

=== added file 'hr_payroll_period/hr_payroll_period_workflow.xml'
--- hr_payroll_period/hr_payroll_period_workflow.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/hr_payroll_period_workflow.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data>
+        
+        <!-- Workflow Definition -->
+        <record id="wkf_payroll_period" model="workflow">
+            <field name="name">hr.payroll.period.basic</field>
+            <field name="osv">hr.payroll.period</field>
+            <field name="on_create">True</field>
+        </record>
+        
+        <!-- Workflow Activities (Stages) -->
+        
+        <record id="act_open" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_payroll_period"/>
+            <field name="name">open</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'open'})</field>
+            <field name="flow_start">True</field>
+        </record>
+        
+        <record id="act_ended" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_payroll_period"/>
+            <field name="name">ended</field>
+            <field name="kind">function</field>
+            <field name="action">set_state_ended()</field>
+        </record>
+        
+        <record id="act_period_locked" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_payroll_period"/>
+            <field name="name">locked</field>
+            <field name="kind">function</field>
+            <field name="action">set_state_locked()</field>
+        </record>
+        
+        <record id="act_generate" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_payroll_period"/>
+            <field name="name">generate</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'generate'})</field>
+        </record>
+        
+        <record id="act_payment" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_payroll_period"/>
+            <field name="name">payment</field>
+            <field name="kind">function</field>
+            <field name="action">write({'state': 'payment'})</field>
+        </record>
+        
+        <record id="act_closed" model="workflow.activity">
+            <field name="wkf_id" ref="wkf_payroll_period"/>
+            <field name="name">closed</field>
+            <field name="kind">function</field>
+            <field name="action">set_state_closed()</field>
+        </record>
+        
+        <!-- Workflow Transitions -->
+        
+        <record id="open2ended" model="workflow.transition">
+            <field name="act_from" ref="act_open"/>
+            <field name="act_to" ref="act_ended"/>
+            <field name="signal">end_period</field>
+        </record>
+        
+        <record id="ended2locked" model="workflow.transition">
+            <field name="act_from" ref="act_ended"/>
+            <field name="act_to" ref="act_period_locked"/>
+            <field name="signal">lock_period</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="locked2ended" model="workflow.transition">
+            <field name="act_from" ref="act_period_locked"/>
+            <field name="act_to" ref="act_ended"/>
+            <field name="signal">unlock_period</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="locked2generate" model="workflow.transition">
+            <field name="act_from" ref="act_period_locked"/>
+            <field name="act_to" ref="act_generate"/>
+            <field name="signal">generate_payslips</field>
+            <field name="group_id" ref="hr_security.group_payroll_manager"/>
+        </record>
+        
+        <record id="generate2ended" model="workflow.transition">
+            <field name="act_from" ref="act_generate"/>
+            <field name="act_to" ref="act_ended"/>
+            <field name="signal">unlock_period</field>
+            <field name="group_id" ref="base.group_hr_manager"/>
+        </record>
+        
+        <record id="generate2payment" model="workflow.transition">
+            <field name="act_from" ref="act_generate"/>
+            <field name="act_to" ref="act_payment"/>
+            <field name="signal">start_payments</field>
+            <field name="group_id" ref="hr_security.group_payroll_manager"/>
+        </record>
+        
+        <record id="payment2closed" model="workflow.transition">
+            <field name="act_from" ref="act_payment"/>
+            <field name="act_to" ref="act_closed"/>
+            <field name="signal">close_period</field>
+            <field name="group_id" ref="hr_security.group_payroll_manager"/>
+        </record>
+        
+    </data>
+</openerp>

=== added directory 'hr_payroll_period/security'
=== added file 'hr_payroll_period/security/ir.model.access.csv'
--- hr_payroll_period/security/ir.model.access.csv	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/security/ir.model.access.csv	2013-09-27 21:15:53 +0000
@@ -0,0 +1,35 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_hr_payroll_period_user,access_hr_payroll_period,model_hr_payroll_period,hr_security.group_payroll_user,1,1,1,0
+access_hr_payroll_period_manager,access_hr_payroll_period,model_hr_payroll_period,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payroll_period_hrm,access_hr_payroll_period,model_hr_payroll_period,base.group_hr_manager,1,1,0,0
+access_hr_payroll_period_schedule_user,access_hr_payroll_period_schedule,model_hr_payroll_period_schedule,hr_security.group_payroll_user,1,0,0,0
+access_hr_payroll_period_schedule_manager,access_hr_payroll_period_schedule,model_hr_payroll_period_schedule,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payroll_period_schedule_hro,access_hr_payroll_period_schedule,model_hr_payroll_period_schedule,base.group_hr_user,1,0,0,0
+access_hr_payslip_amendment_hruser,access_hr_payslip_amendment,model_hr_payslip_amendment,base.group_hr_user,1,1,1,1
+access_hr_payslip_amendment_user,access_hr_payslip_amendment,model_hr_payslip_amendment,hr_security.group_payroll_user,1,1,1,1
+access_hr_payslip_exception_hrm,access_hr_payslip_exception,model_hr_payslip_exception,base.group_hr_manager,1,0,0,0
+access_hr_payslip_exception_rule_hrm,access_hr_payslip_exception_rule,model_hr_payslip_exception_rule,base.group_hr_manager,1,0,0,0
+access_hr_payslip_exception_pm,access_hr_payslip_exception,model_hr_payslip_exception,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payslip_exception_rule_pm,access_hr_payslip_exception_rule,model_hr_payslip_exception_rule,hr_security.group_payroll_manager,1,1,1,1
+access_hr_contract_pm,access_hr_contract,model_hr_contract,hr_security.group_payroll_manager,1,0,0,0
+access_hr_payroll_structure_pm,hr.payroll.structure,hr_payroll.model_hr_payroll_structure,hr_security.group_payroll_manager,1,1,1,1
+access_hr_contribution_register_pm,hr.contribution.register,hr_payroll.model_hr_contribution_register,hr_security.group_payroll_manager,1,1,1,1
+access_hr_salary_rule_category_pm,hr.salary.rule.category,hr_payroll.model_hr_salary_rule_category,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payslip_pm,hr.payslip,hr_payroll.model_hr_payslip,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payslip_line_pm,hr.payslip.line,hr_payroll.model_hr_payslip_line,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payslip_input_pm,hr.payslip.input,hr_payroll.model_hr_payslip_input,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payslip_worked_days _pm,hr.payslip.worked_days,hr_payroll.model_hr_payslip_worked_days,hr_security.group_payroll_manager,1,1,1,1
+access_hr_payslip_run_pm,hr.payslip.run,hr_payroll.model_hr_payslip_run,hr_security.group_payroll_manager,1,1,1,1
+access_hr_rule_input_pm,hr.rule.input,hr_payroll.model_hr_rule_input,hr_security.group_payroll_manager,1,1,1,1
+access_hr_salary_rule_pm,hr.salary.rule,hr_payroll.model_hr_salary_rule,hr_security.group_payroll_manager,1,1,1,1
+access_hr_schedule_pm,access_hr_schedule,hr_schedule.model_hr_schedule,hr_security.group_payroll_manager,1,0,0,0
+access_hr_schedule_detail_pm,access_hr_schedule_detail,hr_schedule.model_hr_schedule_detail,hr_security.group_payroll_manager,1,0,0,0
+access_hr_schedule_template_pm,access_hr_schedule_template,hr_schedule.model_hr_schedule_template,hr_security.group_payroll_manager,1,0,0,0
+access_hr_schedule_template_worktime_pm,access_hr_schedule_template_worktime,hr_schedule.model_hr_schedule_template_worktime,hr_security.group_payroll_manager,1,0,0,0
+access_hr_schedule_alert_pm,access_hr_schedule_alert,hr_schedule.model_hr_schedule_alert,hr_security.group_payroll_manager,1,0,0,0
+access_hr_schedule_alert_rule_pm,access_hr_schedule_alert_rule,hr_schedule.model_hr_schedule_alert_rule,hr_security.group_payroll_manager,1,0,0,0
+access_hr_payroll_register_pm,access_hr_payroll_register,hr_payroll_register.model_hr_payroll_register,hr_security.group_payroll_manager,1,1,1,1
+access_hr_attendance_pm,access_hr_attendance,hr_attendance.model_hr_attendance,hr_security.group_payroll_manager,1,0,0,0
+access_hr_holidays_pm,access_hr_holidays,hr_holidays.model_hr_holidays,hr_security.group_payroll_manager,1,0,0,0
+access_hr_holidays_status_pm,access_hr_holidays,hr_holidays.model_hr_holidays_status,hr_security.group_payroll_manager,1,0,0,0
+access_hr_employee_children_pm,hr.employee.children_pm,hr_family.model_hr_employee_children,hr_security.group_payroll_manager,1,0,0,0

=== added file 'hr_payroll_period/security/ir_rule.xml'
--- hr_payroll_period/security/ir_rule.xml	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/security/ir_rule.xml	2013-09-27 21:15:53 +0000
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+    <data noupdate="1">
+
+        <!-- Allow Payroll Manager access to all attendance records -->
+        <record id="property_rule_attendace_pm" model="ir.rule">
+            <field name="name">Payroll Manager Attendance</field>
+            <field model="ir.model" name="model_id" ref="model_hr_attendance"/>
+            <field name="domain_force">[(1,'=',1)]</field>
+            <field name="groups" eval="[(4,ref('hr_security.group_payroll_manager'))]"/>
+        </record>
+        
+        <!-- Allow Payroll Manager to access holidays/leaves -->
+        <record id="property_rule_holidays_pm" model="ir.rule">
+            <field name="name">Payroll Manager Holidays</field>
+            <field model="ir.model" name="model_id" ref="hr_holidays.model_hr_holidays"/>
+            <field name="domain_force">[(1,'=',1)]</field>
+            <field name="groups" eval="[(4,ref('hr_security.group_payroll_manager'))]"/>
+        </record>
+
+    </data>
+</openerp>
+

=== added directory 'hr_payroll_period/wizard'
=== added file 'hr_payroll_period/wizard/__init__.py'
--- hr_payroll_period/wizard/__init__.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/wizard/__init__.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,22 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import payroll_period_end

=== added file 'hr_payroll_period/wizard/payroll_period_end.py'
--- hr_payroll_period/wizard/payroll_period_end.py	1970-01-01 00:00:00 +0000
+++ hr_payroll_period/wizard/payroll_period_end.py	2013-09-27 21:15:53 +0000
@@ -0,0 +1,1162 @@
+#-*- coding:utf-8 -*-
+#
+#
+#    Copyright (C) 2013 Michael Telahun Makonnen <mmakonnen@xxxxxxxxx>.
+#    All Rights Reserved.
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import logging
+import math
+import netsvc
+from datetime import datetime, timedelta
+from dateutil.relativedelta import relativedelta
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as OEDATETIME_FORMAT
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OEDATE_FORMAT
+from openerp.tools.translate import _
+from osv import fields, osv
+from pytz import timezone
+
+_logger = logging.getLogger(__name__)
+
+
+class payroll_period_end_1(osv.osv_memory):
+
+    _name = 'hr.payroll.period.end.1'
+    _description = 'End of Payroll Period Wizard Step 1'
+
+    _change_res = {
+        'br100': 0,
+        'br50': 0,
+        'br10': 0,
+        'br5': 0,
+        'br1': 0,
+        'cent50': 0,
+        'cent25': 0,
+        'cent10': 0,
+        'cent05': 0,
+        'cent01': 0,
+        'done': False,
+    }
+
+    _columns = {
+        'period_id': fields.integer('Period ID'),
+        'is_ended': fields.boolean('Past End Day?'),
+        'public_holiday_ids': fields.many2many('hr.holidays.public.line', 'hr_holidays_pay_period_rel', 'holiday_id', 'period_id', 'Public Holidays', readonly=True),
+        'alert_critical': fields.integer('Critical Severity', readonly=True),
+        'alert_high': fields.integer('High Severity', readonly=True),
+        'alert_medium': fields.integer('Medium Severity', readonly=True),
+        'alert_low': fields.integer('Low Severity', readonly=True),
+        'pex_critical': fields.integer('Critical', readonly=True),
+        'pex_high': fields.integer('High', readonly=True),
+        'pex_medium': fields.integer('Medium', readonly=True),
+        'pex_low': fields.integer('Low', readonly=True),
+        'locked': fields.boolean('Is Period Locked?', readonly=True),
+        'can_unlock': fields.boolean('Can Unlock Period?', readonly=True),
+        'payslips': fields.boolean('Have Pay Slips Been Generated?', readonly=True),
+        'ps_gen