114 lines
6.0 KiB
Python
114 lines
6.0 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.tools import float_round
|
|
|
|
|
|
class HrPayslipWorkedDays(models.Model):
|
|
_name = 'hr.payslip.worked_days'
|
|
_description = 'Payslip Worked Days'
|
|
_order = 'payslip_id, sequence'
|
|
|
|
name = fields.Char(compute='_compute_name', store=True, string='Description', readonly=False)
|
|
payslip_id = fields.Many2one('hr.payslip', string='Pay Slip', required=True, ondelete='cascade', index=True)
|
|
date_from = fields.Date(string='From', related="payslip_id.date_from", store=True)
|
|
employee_id = fields.Many2one('hr.employee', string='Employee', related='payslip_id.employee_id', store=True)
|
|
sequence = fields.Integer(required=True, index=True, default=10)
|
|
code = fields.Char(string='Code', related='work_entry_type_id.code')
|
|
work_entry_type_id = fields.Many2one('hr.work.entry.type', string='Type', required=True, help="The code that can be used in the salary rules")
|
|
number_of_days = fields.Float(string='Number of Days')
|
|
number_of_hours = fields.Float(string='Number of Hours')
|
|
is_paid = fields.Boolean(compute='_compute_is_paid', store=True)
|
|
amount = fields.Monetary(string='Amount', compute='_compute_amount', store=True, copy=True)
|
|
version_id = fields.Many2one(related='payslip_id.version_id', string='Contract',
|
|
help="The contract this worked days should be applied to")
|
|
currency_id = fields.Many2one('res.currency', related='payslip_id.currency_id')
|
|
ytd = fields.Monetary(string='YTD')
|
|
|
|
@api.depends(
|
|
'work_entry_type_id', 'payslip_id', 'payslip_id.struct_id',
|
|
'payslip_id.employee_id', 'payslip_id.version_id', 'payslip_id.struct_id', 'payslip_id.date_from', 'payslip_id.date_to')
|
|
def _compute_is_paid(self):
|
|
unpaid = {struct.id: struct.unpaid_work_entry_type_ids.ids for struct in self.mapped('payslip_id.struct_id')}
|
|
for worked_days in self:
|
|
worked_days.is_paid = (worked_days.work_entry_type_id.id not in unpaid[worked_days.payslip_id.struct_id.id]) if worked_days.payslip_id.struct_id.id in unpaid else False
|
|
|
|
@api.depends(
|
|
'is_paid', 'number_of_hours', 'payslip_id', 'version_id.wage', 'version_id.hourly_wage', 'payslip_id.sum_worked_hours',
|
|
'work_entry_type_id.amount_rate', 'work_entry_type_id.is_extra_hours')
|
|
def _compute_amount(self):
|
|
for worked_days in self:
|
|
if worked_days.payslip_id.edited or worked_days.payslip_id.state != 'draft':
|
|
continue
|
|
if not worked_days.version_id or worked_days.code == 'OUT':
|
|
worked_days.amount = 0
|
|
continue
|
|
version = worked_days.payslip_id.version_id
|
|
amount_rate = worked_days.work_entry_type_id.amount_rate
|
|
if worked_days.payslip_id.wage_type == "hourly":
|
|
hourly_rate = version.hourly_wage
|
|
else:
|
|
attendance_hours = sum(
|
|
wd.number_of_hours for wd in worked_days.payslip_id.worked_days_line_ids
|
|
if not wd.work_entry_type_id.is_extra_hours
|
|
) or 1
|
|
hourly_rate = version.contract_wage / attendance_hours
|
|
worked_days.amount = hourly_rate * worked_days.number_of_hours * amount_rate if worked_days.is_paid else 0
|
|
|
|
def _is_half_day(self):
|
|
self.ensure_one()
|
|
work_hours = self.payslip_id._get_worked_day_lines_hours_per_day()
|
|
# For refunds number of days is negative
|
|
return abs(self.number_of_days) < 1 or float_round(self.number_of_hours / self.number_of_days, 2) < work_hours
|
|
|
|
@api.depends('work_entry_type_id', 'number_of_days', 'number_of_hours', 'payslip_id')
|
|
def _compute_name(self):
|
|
if not self.payslip_id:
|
|
return
|
|
|
|
to_check_public_holiday = dict(
|
|
self.env['resource.calendar.leaves']._read_group(
|
|
[
|
|
('resource_id', '=', False),
|
|
('work_entry_type_id', 'in', self.mapped('work_entry_type_id').ids),
|
|
('date_from', '<=', max(self.payslip_id.mapped('date_to'))),
|
|
('date_to', '>=', min(self.payslip_id.mapped('date_from'))),
|
|
],
|
|
['work_entry_type_id'],
|
|
['id:recordset']
|
|
)
|
|
)
|
|
work_entries = {
|
|
(employee, date): we
|
|
for employee, date, we in self.env['hr.work.entry']._read_group(
|
|
domain=[
|
|
('date', '<=', max(self.payslip_id.mapped('date_to'))),
|
|
('date', '>=', min(self.payslip_id.mapped('date_from'))),
|
|
('employee_id', 'in', self.payslip_id.employee_id.ids)
|
|
],
|
|
groupby=['employee_id', 'date:day'],
|
|
aggregates=['id:recordset'])}
|
|
|
|
for worked_days in self:
|
|
public_holidays = to_check_public_holiday.get(worked_days.work_entry_type_id, '')
|
|
holidays = public_holidays and public_holidays.filtered(lambda p:
|
|
(p.calendar_id.id == worked_days.payslip_id.version_id.resource_calendar_id.id or not p.calendar_id.id)
|
|
and p.date_from.date() <= worked_days.payslip_id.date_to
|
|
and p.date_to.date() >= worked_days.payslip_id.date_from
|
|
and p.company_id == worked_days.payslip_id.company_id)
|
|
actual_holidays = self.env['resource.calendar.leaves']
|
|
if holidays:
|
|
for holiday in holidays:
|
|
work_entry_list = work_entries.get(
|
|
(worked_days.payslip_id.employee_id, holiday.date_from.date()),
|
|
self.env['hr.work.entry']
|
|
)
|
|
if any(we.code == holiday.work_entry_type_id.code for we in work_entry_list):
|
|
actual_holidays |= holiday
|
|
if actual_holidays:
|
|
name = (', '.join(actual_holidays.mapped('name')))
|
|
else:
|
|
name = worked_days.work_entry_type_id.name or ''
|
|
half_day = worked_days._is_half_day()
|
|
worked_days.name = name + (_(' (Half-Day)') if half_day else '')
|