forked from Mapan/odoo17e
151 lines
6.9 KiB
Python
151 lines
6.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from collections import defaultdict
|
|
from datetime import datetime, date, time
|
|
from dateutil.relativedelta import relativedelta
|
|
import pytz
|
|
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import UserError
|
|
from odoo.osv import expression
|
|
from odoo.tools import format_date
|
|
|
|
|
|
class HrPayslipEmployees(models.TransientModel):
|
|
_name = 'hr.payslip.employees'
|
|
_description = 'Generate payslips for all selected employees'
|
|
|
|
def _get_available_contracts_domain(self):
|
|
return [('contract_ids.state', 'in', ('open', 'close')), ('company_id', '=', self.env.company.id)]
|
|
|
|
def _get_employees(self):
|
|
active_employee_ids = self.env.context.get('active_employee_ids', False)
|
|
if active_employee_ids:
|
|
return self.env['hr.employee'].browse(active_employee_ids)
|
|
# YTI check dates too
|
|
return self.env['hr.employee'].search(self._get_available_contracts_domain())
|
|
|
|
employee_ids = fields.Many2many('hr.employee', 'hr_employee_group_rel', 'payslip_id', 'employee_id', 'Employees',
|
|
default=lambda self: self._get_employees(), required=True,
|
|
compute='_compute_employee_ids', store=True, readonly=False)
|
|
structure_id = fields.Many2one('hr.payroll.structure', string='Salary Structure')
|
|
department_id = fields.Many2one('hr.department')
|
|
|
|
@api.depends('department_id')
|
|
def _compute_employee_ids(self):
|
|
for wizard in self:
|
|
domain = wizard._get_available_contracts_domain()
|
|
if wizard.department_id:
|
|
domain = expression.AND([
|
|
domain,
|
|
[('department_id', 'child_of', self.department_id.id)]
|
|
])
|
|
wizard.employee_ids = self.env['hr.employee'].search(domain)
|
|
|
|
def _filter_contracts(self, contracts):
|
|
# Could be overriden to avoid having 2 'end of the year bonus' payslips, etc.
|
|
return contracts
|
|
|
|
def compute_sheet(self):
|
|
self.ensure_one()
|
|
if not self.env.context.get('active_id'):
|
|
from_date = fields.Date.to_date(self.env.context.get('default_date_start'))
|
|
end_date = fields.Date.to_date(self.env.context.get('default_date_end'))
|
|
today = fields.date.today()
|
|
first_day = today + relativedelta(day=1)
|
|
last_day = today + relativedelta(day=31)
|
|
if from_date == first_day and end_date == last_day:
|
|
batch_name = from_date.strftime('%B %Y')
|
|
else:
|
|
batch_name = _('From %s to %s', format_date(self.env, from_date), format_date(self.env, end_date))
|
|
payslip_run = self.env['hr.payslip.run'].create({
|
|
'name': batch_name,
|
|
'date_start': from_date,
|
|
'date_end': end_date,
|
|
})
|
|
else:
|
|
payslip_run = self.env['hr.payslip.run'].browse(self.env.context.get('active_id'))
|
|
|
|
employees = self.with_context(active_test=False).employee_ids
|
|
if not employees:
|
|
raise UserError(_("You must select employee(s) to generate payslip(s)."))
|
|
|
|
#Prevent a payslip_run from having multiple payslips for the same employee
|
|
employees -= payslip_run.slip_ids.employee_id
|
|
success_result = {
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'hr.payslip.run',
|
|
'views': [[False, 'form']],
|
|
'res_id': payslip_run.id,
|
|
}
|
|
if not employees:
|
|
return success_result
|
|
|
|
payslips = self.env['hr.payslip']
|
|
Payslip = self.env['hr.payslip']
|
|
|
|
contracts = employees._get_contracts(
|
|
payslip_run.date_start, payslip_run.date_end, states=['open', 'close']
|
|
).filtered(lambda c: c.active)
|
|
contracts.generate_work_entries(payslip_run.date_start, payslip_run.date_end)
|
|
work_entries = self.env['hr.work.entry'].search([
|
|
('date_start', '<=', payslip_run.date_end + relativedelta(days=1)),
|
|
('date_stop', '>=', payslip_run.date_start + relativedelta(days=-1)),
|
|
('employee_id', 'in', employees.ids),
|
|
])
|
|
for slip in payslip_run.slip_ids:
|
|
slip_tz = pytz.timezone(slip.contract_id.resource_calendar_id.tz)
|
|
utc = pytz.timezone('UTC')
|
|
date_from = slip_tz.localize(datetime.combine(slip.date_from, time.min)).astimezone(utc).replace(tzinfo=None)
|
|
date_to = slip_tz.localize(datetime.combine(slip.date_to, time.max)).astimezone(utc).replace(tzinfo=None)
|
|
payslip_work_entries = work_entries.filtered_domain([
|
|
('contract_id', '=', slip.contract_id.id),
|
|
('date_stop', '<=', date_to),
|
|
('date_start', '>=', date_from),
|
|
])
|
|
payslip_work_entries._check_undefined_slots(slip.date_from, slip.date_to)
|
|
|
|
|
|
if(self.structure_id.type_id.default_struct_id == self.structure_id):
|
|
work_entries = work_entries.filtered(lambda work_entry: work_entry.state != 'validated')
|
|
if work_entries._check_if_error():
|
|
work_entries_by_contract = defaultdict(lambda: self.env['hr.work.entry'])
|
|
|
|
for work_entry in work_entries.filtered(lambda w: w.state == 'conflict'):
|
|
work_entries_by_contract[work_entry.contract_id] |= work_entry
|
|
|
|
for contract, work_entries in work_entries_by_contract.items():
|
|
conflicts = work_entries._to_intervals()
|
|
time_intervals_str = "\n - ".join(['', *["%s -> %s (%s)" % (s[0], s[1], s[2].employee_id.name) for s in conflicts._items]])
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Some work entries could not be validated.'),
|
|
'message': _('Time intervals to look for:%s', time_intervals_str),
|
|
'sticky': False,
|
|
}
|
|
}
|
|
|
|
|
|
default_values = Payslip.default_get(Payslip.fields_get())
|
|
payslips_vals = []
|
|
for contract in self._filter_contracts(contracts):
|
|
values = dict(default_values, **{
|
|
'name': _('New Payslip'),
|
|
'employee_id': contract.employee_id.id,
|
|
'payslip_run_id': payslip_run.id,
|
|
'date_from': payslip_run.date_start,
|
|
'date_to': payslip_run.date_end,
|
|
'contract_id': contract.id,
|
|
'struct_id': self.structure_id.id or contract.structure_type_id.default_struct_id.id,
|
|
})
|
|
payslips_vals.append(values)
|
|
payslips = Payslip.with_context(tracking_disable=True).create(payslips_vals)
|
|
payslips._compute_name()
|
|
payslips.compute_sheet()
|
|
payslip_run.state = 'verify'
|
|
|
|
return success_result
|