291 lines
12 KiB
Python
291 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo import api, fields, models, tools
|
|
from odoo.exceptions import ValidationError, UserError
|
|
from dateutil.relativedelta import relativedelta
|
|
from datetime import date
|
|
from datetime import timedelta
|
|
|
|
|
|
class HrContract(models.Model):
|
|
_inherit = "hr.contract"
|
|
_description = "员工合同拓展"
|
|
|
|
code = fields.Char(string="编号", default=lambda self: self.env['ir.sequence'].next_by_code('contract_code'))
|
|
name = fields.Char(string="合同名称", compute='_compute_name', readonly=False)
|
|
is_annual_salary = fields.Boolean(string="是否年薪")
|
|
resource_calendar_id = fields.Many2one('resource.calendar', string='考勤时间表')
|
|
state = fields.Selection([
|
|
('draft', '草稿'),
|
|
('open', '运行中'),
|
|
('close', '完成'),
|
|
('cancel', '已中止'),
|
|
], string='状态', default='draft', readonly=True, copy=False)
|
|
date_of_entry = fields.Date(related='employee_id.entry_date', string="入职时间")
|
|
salary_group_id = fields.Many2one('yuthon.salary.group', string="工资单分组", store=True, index=True)
|
|
is_internship = fields.Boolean(string="实习期", compute='_compute_name', store=True)
|
|
date_end = fields.Date(string="合同结束时间")
|
|
termination_date = fields.Date(string="中止时间")
|
|
reason_suspension = fields.Selection([
|
|
('turnover', '离职'),
|
|
('died', '意外离世'),
|
|
], string='中止原因')
|
|
work_entry_source = fields.Selection([
|
|
('calendar', '考勤安排'),
|
|
('attendance', 'Attendances'),
|
|
('planning', '特殊排版')])
|
|
is_probation = fields.Boolean(string="试用期")
|
|
start_date = fields.Date(string="试用开始日期", required=True, default=fields.Date.today)
|
|
end_date = fields.Date(string="试用结束日期", required=True,
|
|
default=lambda self: fields.Date.today() + relativedelta(months=+3) - relativedelta(days=1))
|
|
salary_day = fields.Float(string="日薪")
|
|
party_id = fields.Many2one('yuthon.party.setting', string='甲方(用人单位)')
|
|
representatives = fields.Char(related='party_id.representatives', string='法定代表人或委托代理人')
|
|
contract_type_id = fields.Many2one('hr.contract.type', default=lambda self: self.env['hr.contract.type'].search(
|
|
[('name', '=', '正式合同(一签)')],
|
|
limit=1
|
|
).id or False, string='合同类型')
|
|
economy_type = fields.Char(related='party_id.economy_type', string='经济类型')
|
|
address = fields.Text(related='party_id.address', string='通讯地址')
|
|
contact = fields.Char(related='party_id.contact', string='联系人')
|
|
phone = fields.Char(related='party_id.phone', string='电话')
|
|
documents_ids = fields.Many2many('ir.attachment', string="合同附件")
|
|
current_contract = fields.Boolean(string="当前合同", default=True, compute='_compute_current_contract', store=True)
|
|
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company', search='_search_part_of_company')
|
|
|
|
@api.depends('date_start', 'date_end')
|
|
def _compute_current_contract(self):
|
|
today = date.today()
|
|
for contract in self:
|
|
start_valid = contract.date_start and contract.date_start <= today
|
|
end_valid = not contract.date_end or contract.date_end >= today
|
|
contract.current_contract = start_valid and end_valid
|
|
|
|
|
|
@api.onchange('date_start')
|
|
def _onchange_date_end(self):
|
|
for record in self:
|
|
if record.date_start:
|
|
record.date_end = record.date_start + relativedelta(years=1) - relativedelta(days=1)
|
|
|
|
@api.onchange('contract_type_id')
|
|
def _onchange_contract_type_id(self):
|
|
for record in self:
|
|
if record.contract_type_id:
|
|
if record.contract_type_id.name == '正式合同(一签)':
|
|
record.date_end = record.date_start + relativedelta(years=1) - relativedelta(days=1)
|
|
elif record.contract_type_id.name == '正式合同(二签)':
|
|
record.date_end = record.date_start + relativedelta(years=3) - relativedelta(days=1)
|
|
|
|
@api.onchange('date_start')
|
|
def _onchange_date_start(self):
|
|
for record in self:
|
|
if record.date_start and record.state == 'open' and record.employee_id:
|
|
record.employee_id.entry_date = record.date_start
|
|
|
|
def renew(self):
|
|
contract_ids = self.env["hr.contract"].browse(
|
|
self._context.get('active_ids', self._context.get('active_id')))
|
|
for record in contract_ids:
|
|
if len(record) > 1:
|
|
raise UserError("只能选择一个合同续签")
|
|
return {
|
|
'name': '续签',
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'yuthon.renewal.approval',
|
|
'view_mode': 'form',
|
|
'context': {
|
|
'default_employee_id': self.employee_id.id,
|
|
'default_current_status': self.date_end,
|
|
'default_contract_id': self.id,
|
|
}
|
|
}
|
|
|
|
def pub_leaving_employee(self):
|
|
record_id = self.env["hr.contract"].browse(self._context.get('active_ids', self._context.get('active_id')))
|
|
if len(record_id) != 1:
|
|
raise UserError("请选择一个合同")
|
|
form_id = self.env.ref('yuthon_hr_employee_approval.view_yuthon_leaving_approval_form').id
|
|
return {
|
|
"type": "ir.actions.act_window",
|
|
"name": "离职申请",
|
|
"res_model": "yuthon.leaving.approval",
|
|
"views": [[form_id, "form"]],
|
|
"context": {
|
|
'default_employee_id': self.employee_id.id,
|
|
}
|
|
}
|
|
|
|
def write(self, vals):
|
|
"""在写入时自动更新合同状态"""
|
|
today = fields.Date.today()
|
|
update_state = False
|
|
if 'date_start' in vals:
|
|
update_state = True
|
|
if 'date_end' in vals:
|
|
update_state = True
|
|
if 'termination_date' in vals:
|
|
update_state = True
|
|
if update_state:
|
|
for contract in self:
|
|
# 获取最新的开始日期
|
|
start_date = fields.Date.to_date(vals.get('date_start', contract.date_start))
|
|
|
|
# 获取最新的结束日期(空值视为无限大)
|
|
end_date = vals.get('date_end', contract.date_end)
|
|
end_date = fields.Date.to_date(end_date) if end_date else None
|
|
|
|
# 获取最新的中止时间(空值视为未中止)
|
|
termination_date = vals.get('termination_date', contract.termination_date)
|
|
termination_date = fields.Date.to_date(termination_date) if termination_date else None
|
|
|
|
# 判断合同状态
|
|
if termination_date and termination_date < today: # 新增:优先判断是否已中止
|
|
new_state = 'cancel'
|
|
elif start_date > today:
|
|
new_state = 'draft'
|
|
elif end_date and end_date < today:
|
|
new_state = 'close'
|
|
else:
|
|
new_state = 'open'
|
|
|
|
# 更新当前合同的状态
|
|
if 'state' not in vals:
|
|
vals['state'] = new_state
|
|
|
|
return super(HrContract, self).write(vals)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals):
|
|
"""在创建时自动更新合同状态"""
|
|
today = fields.Date.today()
|
|
|
|
start_date = fields.Date.to_date(vals.get('date_start'))
|
|
end_date = vals.get('date_end')
|
|
end_date = fields.Date.to_date(end_date) if end_date else None
|
|
termination_date = vals.get('termination_date')
|
|
termination_date = fields.Date.to_date(termination_date) if termination_date else None
|
|
|
|
# 根据日期判断状态
|
|
if termination_date and termination_date < today:
|
|
vals['state'] = 'cancel'
|
|
elif start_date > today:
|
|
vals['state'] = 'draft'
|
|
elif end_date and end_date < today:
|
|
vals['state'] = 'close'
|
|
else:
|
|
vals['state'] = 'open'
|
|
|
|
return super(HrContract, self).create(vals)
|
|
|
|
def update_contract_states(self):
|
|
today = date.today()
|
|
active_contracts = self.search([
|
|
('state', 'not in', ['cancel']),
|
|
])
|
|
for contract in active_contracts:
|
|
new_state = 'open' if not contract.date_end or contract.date_end >= today else 'close'
|
|
if new_state == 'open':
|
|
conflicting_contracts = self.search([
|
|
('id', '!=', contract.id),
|
|
('employee_id', '=', contract.employee_id.id),
|
|
('state', '=', 'open'),
|
|
])
|
|
if conflicting_contracts:
|
|
continue
|
|
contract.state = new_state
|
|
|
|
@api.depends_context('uid', 'company')
|
|
@api.depends('company_id')
|
|
def _compute_is_company(self):
|
|
active_company_ids = self.env.companies
|
|
for employee in self:
|
|
employee.is_company = employee.company_id in active_company_ids
|
|
|
|
def _search_part_of_company(self, operator, value):
|
|
if operator not in ('=', '!=') or not isinstance(value, bool):
|
|
raise UserError(('Operation not supported'))
|
|
company_ids = self.env.companies.ids # 获取公司ID列表
|
|
# 处理反向操作符
|
|
if not value:
|
|
operator = '!=' if operator == '=' else '='
|
|
# 使用company_id in company_ids的结构
|
|
if operator == '=':
|
|
return [('company_id', 'in', company_ids)]
|
|
else: # operator == '!='
|
|
return [('company_id', 'not in', company_ids)]
|
|
|
|
# @api.onchange('resource_calendar_id')
|
|
# def _onchange_resource_calendar_id(self):
|
|
# if self.resource_calendar_id:
|
|
# self.employee_id.sudo().write({'resource_calendar_id': self.resource_calendar_id.id})
|
|
|
|
@api.depends('contract_type_id')
|
|
def _compute_simplified_name(self):
|
|
for record in self:
|
|
if record.contract_type_id and record.employee_id:
|
|
record.name = record.contract_type_id.name + '合同'
|
|
else:
|
|
record.name = ' '
|
|
if record.contract_type_id:
|
|
if record.contract_type_id.name == '实习协议':
|
|
record.is_internship = True
|
|
else:
|
|
record.is_internship = False
|
|
|
|
@api.depends('contract_type_id')
|
|
def _compute_name(self):
|
|
for record in self:
|
|
if record.contract_type_id and record.employee_id:
|
|
record.name = record.employee_id.name + record.contract_type_id.name + '合同'
|
|
else:
|
|
record.name = ' '
|
|
if record.contract_type_id:
|
|
if record.contract_type_id.name == '实习协议':
|
|
record.is_internship = True
|
|
else:
|
|
record.is_internship = False
|
|
|
|
def action_hr_contract(self):
|
|
return {
|
|
"type": "ir.actions.act_window",
|
|
"name": "批量导入合同",
|
|
"res_model": "yuthon.contract.wizard",
|
|
"view_mode": "form",
|
|
"target": "new",
|
|
}
|
|
|
|
def update_contract(self):
|
|
contract_ids = self.env["hr.contract"].browse(
|
|
self._context.get('active_ids', self._context.get('active_id')))
|
|
for contract in contract_ids:
|
|
company_id = contract.employee_id.company_id
|
|
resource_calendar_id = self.env['resource.calendar'].search([('company_id', '=', company_id.id)], limit=1)
|
|
contract.resource_calendar_id = resource_calendar_id.id
|
|
|
|
@api.onchange('salary_group_id')
|
|
def _compute_salary_group_id(self):
|
|
for contract_id in self:
|
|
employee = self.env['hr.employee'].search([('id', '=', contract_id.employee_id.id)], limit=1)
|
|
if employee:
|
|
employee.salary_group_id = contract_id.salary_group_id.id
|
|
else:
|
|
employee.salary_group_id = False
|
|
|
|
@api.onchange('is_annual_salary')
|
|
def _compute_is_annual_salary(self):
|
|
for contract_id in self:
|
|
employee = self.env['hr.employee'].search([('id', '=', contract_id.employee_id.id)], limit=1)
|
|
if employee:
|
|
employee.is_annual_salary = contract_id.is_annual_salary
|
|
else:
|
|
employee.is_annual_salary = False
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
contract = super(HrContract, self).create(vals)
|
|
if contract.employee_id:
|
|
contract.employee_id.write({
|
|
'contract_id': contract.id,
|
|
})
|
|
return contract
|