1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py
2024-12-10 09:04:09 +07:00

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