Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMP] hr_payroll: improve general usability #30110

Merged
merged 2 commits into from
Jan 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions addons/hr_contract/models/hr_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,18 @@ def _get_contracts(self, date_from, date_to):
"""
Returns the contracts of the employee between date_from and date_to
"""
self.ensure_one()
# a contract is valid if it ends between the given dates
if not self:
employees = self.env['hr.employee'].search([])
else:
employees = self
clause_1 = ['&', ('date_end', '<=', date_to), ('date_end', '>=', date_from)]
# OR if it starts between the given dates
clause_2 = ['&', ('date_start', '<=', date_to), ('date_start', '>=', date_from)]
# OR if it starts before the date_from and finish after the date_end (or never finish)
clause_3 = ['&', ('date_start', '<=', date_from), '|', ('date_end', '=', False), ('date_end', '>=', date_to)]
clause_final = expression.AND([
[('employee_id', '=', self.id), ('state', '=', 'open')],
[('employee_id', 'in', employees.ids), ('state', '=', 'open')],
expression.OR([clause_1, clause_2, clause_3])])
return self.env['hr.contract'].search(clause_final)

Expand Down
8 changes: 7 additions & 1 deletion addons/hr_contract/views/hr_contract_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<field name="employee_id"/>
<field name="department_id" operator="child_of"/>
<field name="state"/>
<filter string="Running" name="running" domain="[('state', '=', 'open')]"/>
<filter string="To Renew" name="to_renew" domain="[('state', '=', 'pending')]"/>
<separator />
<filter string="Current Employee" name="current_employee" domain="[('employee_id.active','=',True)]"/>
Expand Down Expand Up @@ -151,14 +152,19 @@
<field name="name">hr.contract.tree</field>
<field name="model">hr.contract</field>
<field name="arch" type="xml">
<tree string="Contracts" decoration-bf="message_needaction == True">
<tree string="Contracts"
decoration-bf="message_needaction == True"
decoration-info="state == 'draft'"
decoration-muted="state in ('close', 'cancel')"
decoration-danger="state == 'pending'">
<field name="name"/>
<field name="employee_id"/>
<field name="job_id"/>
<field name="type_id"/>
<field name="resource_calendar_id"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="state"/>
<field name="wage" invisible="1"/>
<field name="message_needaction" invisible="1"/>
</tree>
Expand Down
4 changes: 3 additions & 1 deletion addons/hr_payroll/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'sequence': 38,
'summary': 'Manage your employee payroll records',
'description': "",
'installable': True,
'application': True,
'depends': [
'hr_contract',
'hr_holidays',
Expand All @@ -32,7 +34,7 @@
'views/hr_leave_views.xml',
'views/resource_views.xml',
'views/hr_benefit_template.xml',
'wizard/hr_benefit_employee_views.xml',
'views/hr_payroll_menu.xml',
],
'demo': ['data/hr_payroll_demo.xml'],
'qweb': [
Expand Down
20 changes: 17 additions & 3 deletions addons/hr_payroll/models/hr_benefit.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class HrBenefit(models.Model):
active = fields.Boolean(default=True)
employee_id = fields.Many2one('hr.employee', required=True,
domain=lambda self: [('contract_ids.state', 'in', ('open', 'pending')), ('company_id', '=', self.env.user.company_id.id)])
date_start = fields.Datetime(required=True, string='Start')
date_stop = fields.Datetime(string='End')
duration = fields.Float(compute='_compute_duration', inverse='_inverse_duration', store=True, string="Hours")
date_start = fields.Datetime(required=True, string='From')
date_stop = fields.Datetime(string='To')
duration = fields.Float(compute='_compute_duration', inverse='_inverse_duration', store=True, string="Period")
benefit_type_id = fields.Many2one('hr.benefit.type')
color = fields.Integer(related='benefit_type_id.color', readonly=True)
state = fields.Selection([
Expand Down Expand Up @@ -73,6 +73,7 @@ def write(self, vals):
vals['active'] = True
if vals['state'] == 'cancelled':
vals['active'] = False
self.mapped('leave_id').action_refuse()
return super(HrBenefit, self).write(vals)

@api.multi
Expand Down Expand Up @@ -283,3 +284,16 @@ class HrBenefitType(models.Model):
help="If the active field is set to false, it will allow you to hide the benefit type without removing it.")
is_leave = fields.Boolean(default=False, string="Leave")

class Contacts(models.Model):
""" Personnal calendar filter """

_name = 'hr.user.benefit.employee'
_description = 'Benefits Employees'

user_id = fields.Many2one('res.users', 'Me', required=True, default=lambda self: self.env.user)
employee_id = fields.Many2one('hr.employee', 'Employee', required=True)
active = fields.Boolean('Active', default=True)

_sql_constraints = [
('user_id_employee_id_unique', 'UNIQUE(user_id,employee_id)', 'You cannot have twice the same employee.')
]
31 changes: 26 additions & 5 deletions addons/hr_payroll/models/hr_employee.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,36 @@ def _compute_payslip_count(self):
for employee in self:
employee.payslip_count = len(employee.slip_ids)

def has_non_validated_benefits(self, date_from, date_to):
return bool(self.env['hr.benefit'].search_count([
('employee_id', 'in', self.ids),
('date_start', '<=', date_to),
('date_stop', '>=', date_from),
('state', 'in', ['draft', 'confirmed'])
]))

@api.multi



@api.model
def generate_benefit(self, date_start, date_stop):

date_start = date_start.replace(tzinfo=pytz.utc)
date_stop = date_stop.replace(tzinfo=pytz.utc)
def _format_datetime(date):
fmt = '%Y-%m-%d %H:%M:%S'
date = datetime.strptime(date, fmt) if isinstance(date, str) else date
return date.replace(tzinfo=pytz.utc) if not date.tzinfo else date

for employee in self:
date_start = _format_datetime(date_start)
date_stop = _format_datetime(date_stop)

current_contracts = self.env['hr.employee']._get_contracts(date_start, date_stop)
current_employees = current_contracts.mapped('employee_id')
mapped_data = dict.fromkeys(current_employees, self.env['hr.contract'])

for contract in current_contracts:
mapped_data[contract.employee_id] |= contract

for employee, contracts in mapped_data.items():
# Approved leaves
emp_leaves = employee.resource_calendar_id.leave_ids.filtered(
lambda r:
Expand All @@ -47,7 +68,7 @@ def generate_benefit(self, date_start, date_stop):
hr_leave.copy_to_benefits()

new_benefits = self.env['hr.benefit']
for contract in employee._get_contracts(date_start, date_stop):
for contract in contracts:

date_start_benefits = max(date_start, datetime.combine(contract.date_start, datetime.min.time()).replace(tzinfo=pytz.utc))
date_stop_benefits = min(date_stop, datetime.combine(contract.date_end or datetime.max.date(), datetime.max.time()).replace(tzinfo=pytz.utc))
Expand Down
2 changes: 1 addition & 1 deletion addons/hr_payroll/models/hr_leave.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,5 @@ def action_validate(self):
def action_refuse(self):
super(HrLeave, self).action_refuse()
benefits = self.env['hr.benefit'].search([('leave_id', 'in', self.ids)])
benefits.write({'display_warning': False, 'leave_id': None})
benefits.write({'display_warning': False, 'active': False})
return True
46 changes: 25 additions & 21 deletions addons/hr_payroll/models/hr_payslip.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@ class HrPayslip(models.Model):
_description = 'Pay Slip'

struct_id = fields.Many2one('hr.payroll.structure', string='Structure',
readonly=True, states={'draft': [('readonly', False)]},
readonly=True, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]},
help='Defines the rules that have to be applied to this payslip, accordingly '
'to the contract chosen. If you let empty the field contract, this field isn\'t '
'mandatory anymore and thus the rules applied will be all the rules set on the '
'structure of all contracts of the employee valid for the chosen period')
name = fields.Char(string='Payslip Name', readonly=True,
states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
number = fields.Char(string='Reference', readonly=True, copy=False,
states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
employee_id = fields.Many2one('hr.employee', string='Employee', required=True, readonly=True,
states={'draft': [('readonly', False)]})
date_from = fields.Date(string='Date From', readonly=True, required=True,
default=lambda self: fields.Date.to_string(date.today().replace(day=1)), states={'draft': [('readonly', False)]})
date_to = fields.Date(string='Date To', readonly=True, required=True,
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
date_from = fields.Date(string='From', readonly=True, required=True,
default=lambda self: fields.Date.to_string(date.today().replace(day=1)), states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
date_to = fields.Date(string='To', readonly=True, required=True,
default=lambda self: fields.Date.to_string((datetime.now() + relativedelta(months=+1, day=1, days=-1)).date()),
states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
# this is chaos: 4 states are defined, 3 are used ('verify' isn't) and 5 exist ('confirm' seems to have existed)
state = fields.Selection([
('draft', 'Draft'),
Expand All @@ -46,32 +46,36 @@ class HrPayslip(models.Model):
\n* If the payslip is confirmed then status is set to \'Done\'.
\n* When user cancel payslip the status is \'Rejected\'.""")
line_ids = fields.One2many('hr.payslip.line', 'slip_id', string='Payslip Lines', readonly=True,
states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
company_id = fields.Many2one('res.company', string='Company', readonly=True, copy=False,
default=lambda self: self.env['res.company']._company_default_get(),
states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
worked_days_line_ids = fields.One2many('hr.payslip.worked_days', 'payslip_id',
string='Payslip Worked Days', copy=True, readonly=True,
states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
input_line_ids = fields.One2many('hr.payslip.input', 'payslip_id', string='Payslip Inputs',
readonly=True, states={'draft': [('readonly', False)]})
readonly=True, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
paid = fields.Boolean(string='Made Payment Order ? ', readonly=True, copy=False,
states={'draft': [('readonly', False)]})
note = fields.Text(string='Internal Note', readonly=True, states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
note = fields.Text(string='Internal Note', readonly=True, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
contract_id = fields.Many2one('hr.contract', string='Contract', readonly=True,
states={'draft': [('readonly', False)]})
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]})
credit_note = fields.Boolean(string='Credit Note', readonly=True,
states={'draft': [('readonly', False)]},
states={'draft': [('readonly', False)], 'verify': [('readonly', False)]},
help="Indicates this payslip has a refund of another")
payslip_run_id = fields.Many2one('hr.payslip.run', string='Payslip Batches', readonly=True,
copy=False, states={'draft': [('readonly', False)]})
payslip_count = fields.Integer(compute='_compute_payslip_count', string="Payslip Computation Details")
payslip_run_id = fields.Many2one('hr.payslip.run', string='Batche Name', readonly=True,
copy=False, states={'draft': [('readonly', False)], 'verify': [('readonly', False)]}, ondelete='cascade')
compute_date = fields.Date('Computed On')

basic_wage = fields.Monetary(compute='_compute_basic_net')
net_wage = fields.Monetary(compute='_compute_basic_net')
currency_id = fields.Many2one(related='contract_id.currency_id')

@api.multi
def _compute_payslip_count(self):
def _compute_basic_net(self):
for payslip in self:
payslip.payslip_count = len(payslip.line_ids)
payslip.basic_wage = payslip.get_salary_line_total('BASIC')
payslip.net_wage = payslip.get_salary_line_total('NET')

@api.constrains('date_from', 'date_to')
def _check_dates(self):
Expand Down
11 changes: 11 additions & 0 deletions addons/hr_payroll/security/hr_payroll_security.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,16 @@
<field name="perm_read" eval="0"/>
</record>

<record id="hr_user_benefit_employee" model="ir.rule">
<field name="name">Benefits/Employee calendar filter: only self</field>
<field name="model_id" ref="model_hr_user_benefit_employee"/>
<field name="domain_force">[('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_create" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_unlink" eval="1"/>
<field name="perm_read" eval="0"/>
</record>

</data>
</odoo>
1 change: 1 addition & 0 deletions addons/hr_payroll/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ access_hr_payslip_run,hr.payslip.run,model_hr_payslip_run,hr_payroll.group_hr_pa
access_hr_salary_rule_user,hr.salary.rule.user,model_hr_salary_rule,hr_payroll.group_hr_payroll_user,1,1,1,1
access_hr_benefit,access_hr_benefit,model_hr_benefit,group_hr_payroll_user,1,1,1,1
access_hr_benefit_type,access_hr_benefit_type,model_hr_benefit_type,group_hr_payroll_user,1,1,1,1
access_hr_benefit_employee,hr.benefit.employee,model_hr_user_benefit_employee,group_hr_payroll_user,1,1,1,1
Loading