Files
gzth/yuthon_hr_attendance/models/yuthon_shift_planning.py
T
2025-08-29 09:40:56 +08:00

242 lines
13 KiB
Python

from odoo import api, fields, models, tools, _
from datetime import datetime, time, timedelta
from odoo.exceptions import UserError, ValidationError
from dateutil.relativedelta import relativedelta
class YuthonShiftPlanning(models.Model):
_name = 'yuthon.shift.planning'
_description = '保安部排班安排'
name = fields.Char(string='排班名称', required=True)
illustrate = fields.Char(string='排班说明', default='默认按ABC三组轮排到下个月')
number_of_shifts = fields.Integer(string='轮班次数', default=10)
planning_a_ids = fields.One2many('yuthon.shift.planning.a.line', 'shift_planning_id', string='关联A组')
planning_b_ids = fields.One2many('yuthon.shift.planning.b.line', 'shift_planning_id', string='关联B组')
planning_c_ids = fields.One2many('yuthon.shift.planning.c.line', 'shift_planning_id', string='关联C组')
planning_ever_white_ids = fields.One2many('yuthon.shift.ever.white.line', 'shift_planning_id', string='关联常白班')
planning_ever_noon_ids = fields.One2many('yuthon.shift.ever.noon.line', 'shift_planning_id', string='关联常中班')
select_time = fields.Selection([('1', '一月'), ('2', '二月'), ('3', '三月'), ('4', '四月'),
('5', '五月'), ('6', '六月'), ('7', '七月'), ('8', '八月'),
('9', '九月'), ('10', '十月'), ('11', '十一月'),
('12', '十二月')], string='选择月份', default=lambda self: self._get_default_month())
def confirm(self,next_date=False):
selected_month = int(self.select_time)
year = datetime.now().year
target_month = selected_month if not next_date else (selected_month % 12) + 1
target_year = year if (not next_date or selected_month != 12) else year + 1
start_date = datetime(target_year, target_month, 1)
next_month_first_day = start_date + relativedelta(months=1)
end_date = next_month_first_day - timedelta(days=1)
existing_slots = self.env['planning.slot'].search([
('role_id.name', '=', '保安部'),
('start_datetime', '>=', start_date),
('start_datetime', '<=', end_date),
])
if len(existing_slots) > 0:
raise UserError(f"该月已经有排程,请选择其他时间。")
role_a = self.env['planning.role'].search([('name', '=', '保安部')], limit=1)
role_template_a = self.env['planning.slot.template'].search([('planning_name', '=', '早班'), ('role_id', '=', role_a.id)])
role_template_b = self.env['planning.slot.template'].search([('planning_name', '=', '中班'), ('role_id', '=', role_a.id)])
role_template_c = self.env['planning.slot.template'].search([('planning_name', '=', '夜班'), ('role_id', '=', role_a.id)])
role = [role_template_a, role_template_b, role_template_c]
planning_groups = {
'planning_a_ids': [0, 2, 1], # 早 -> 夜 -> 中
'planning_b_ids': [1, 0, 2], # 中 -> 早 -> 夜
'planning_c_ids': [2, 1, 0], # 夜 -> 中 -> 早
}
weekday_mapping = {
'one': 0, 'two': 1, 'three': 2, 'four': 3, 'five': 4, 'six': 5, 'seven': 6
}
for planning_group, role_order in planning_groups.items():
ever_holidays = {} # 为每个组创建独立的休息日字典
# 获取当前组的员工记录
employee_records = getattr(self, planning_group)
# 处理当前组员工的休息日
for record in employee_records:
if record.employee_id:
resource_id = record.employee_id.resource_id.id
weekday_number = weekday_mapping.get(record.regular_holidays)
if resource_id not in ever_holidays:
ever_holidays[resource_id] = []
if weekday_number is not None: # 检查weekday_number是否有效
ever_holidays[resource_id].append(weekday_number)
for record in employee_records:
current_date = start_date
while current_date < end_date:
for role_index in role_order:
for _ in range(self.number_of_shifts):
employee = record.employee_id
resource_id = employee.resource_id.id
current_weekday = current_date.weekday()
if resource_id in ever_holidays and current_weekday in ever_holidays[resource_id]:
pass # 跳过休息日
else:
if current_date.day == 31:
self.create_planning_slot(record, role_a, role[role_index-2], current_date)
else:
self.create_planning_slot(record, role_a, role[role_index], current_date)
current_date += timedelta(days=1)
if current_date >= end_date:
break
if current_date >= end_date:
break
ever_holidays = {}
# 处理早班员工的休息日
for record in self.planning_ever_white_ids:
if record.employee_id:
resource_id = record.employee_id.resource_id.id
weekday_number = weekday_mapping.get(record.regular_holidays)
if resource_id not in ever_holidays:
ever_holidays[resource_id] = []
ever_holidays[resource_id].append(weekday_number)
# 处理中班员工的休息日
for record in self.planning_ever_noon_ids:
if record.employee_id:
resource_id = record.employee_id.resource_id.id
weekday_number = weekday_mapping.get(record.regular_holidays)
if resource_id not in ever_holidays:
ever_holidays[resource_id] = []
ever_holidays[resource_id].append(weekday_number)
def create_shifts_for_group(employee_records, role_item):
for record in employee_records:
resource_id = record.employee_id.resource_id.id
current_date = start_date
while current_date <= end_date:
current_weekday = current_date.weekday()
if resource_id in ever_holidays and current_weekday in ever_holidays[resource_id]:
pass
else:
self.create_planning_slot(record, role_a, role_item, current_date)
current_date += timedelta(days=1)
if current_date >= end_date:
break
if current_date >= end_date:
break
# 为白班员工创建排班
create_shifts_for_group(self.planning_ever_white_ids, role[0])
# 为中班员工创建排班
create_shifts_for_group(self.planning_ever_noon_ids, role[1])
def create_planning_slot(self, record,role,role_template, current_date):
check_in_start = role_template.start_time
check_out_end = role_template.end_time
start_hour = int(check_in_start)
start_minute = int((check_in_start - start_hour) * 60)
end_hour = int(check_out_end)
end_minute = int((check_out_end - end_hour) * 60)
# 拼接成 datetime 对象
start_datetime = datetime.combine(current_date, datetime.min.time()).replace(
hour=start_hour, minute=start_minute, second=0, microsecond=0
)
end_datetime = datetime.combine(current_date, datetime.min.time()).replace(
hour=end_hour, minute=end_minute, second=0, microsecond=0
)
self.env['planning.slot'].create({
'resource_id': record.employee_id.resource_id.id,
'template_id':role_template.id,
'role_id':role.id,
'start_datetime': start_datetime - timedelta(hours=8),
'end_datetime': end_datetime - timedelta(hours=8),
'allocated_hours':8,
})
def shift_next_planing(self):
self.confirm(next_date=True)
def exchange_button(self):
for record in self:
white_lines = record.planning_ever_white_ids
noon_lines = record.planning_ever_noon_ids
white_data = [{'employee_id': line.employee_id.id, 'sequence': line.sequence,
'regular_holidays': line.regular_holidays} for line in white_lines]
noon_data = [{'employee_id': line.employee_id.id, 'sequence': line.sequence,
'regular_holidays': line.regular_holidays} for line in noon_lines]
white_lines.unlink()
noon_lines.unlink()
for data in noon_data:
record.planning_ever_white_ids = [(0, 0, data)]
for data in white_data:
record.planning_ever_noon_ids = [(0, 0, data)]
def _get_default_month(self):
current_month = datetime.now().month
return str(current_month) # 返回字符串类型的月份
class YuthonShiftPlanningALine(models.Model):
_name = 'yuthon.shift.planning.a.line'
_description = '排班安排A组'
employee_id = fields.Many2one('hr.employee', string='人员',
domain=lambda self: [('department_id.name', 'in', ['保安部', '班组A', '班组B','班组C'])])
sequence = fields.Integer(string='序号')
regular_holidays = fields.Selection([('one', '星期一'), ('two', '星期二'), ('three', '星期三'),
('four', '星期四'), ('five', '星期五'), ('six', '星期六'),
('seven', '星期日')], string='固定休息日')
shift_planning_id = fields.Many2one('yuthon.shift.planning', string='关联排班安排')
class YuthonShiftPlanningBLine(models.Model):
_name = 'yuthon.shift.planning.b.line'
_description = '排班安排B组'
employee_id = fields.Many2one('hr.employee', string='人员',
domain=lambda self: [('department_id.name', 'in', ['保安部', '班组A', '班组B','班组C'])])
sequence = fields.Integer(string='序号')
regular_holidays = fields.Selection([('one', '星期一'), ('two', '星期二'), ('three', '星期三'),
('four', '星期四'), ('five', '星期五'), ('six', '星期六'),
('seven', '星期日')], string='固定休息日')
shift_planning_id = fields.Many2one('yuthon.shift.planning', string='关联排班安排')
class YuthonShiftPlanningCLine(models.Model):
_name = 'yuthon.shift.planning.c.line'
_description = '排班安排C组'
employee_id = fields.Many2one('hr.employee', string='人员',
domain=lambda self: [('department_id.name', 'in', ['保安部', '班组A', '班组B','班组C'])])
sequence = fields.Integer(string='序号')
regular_holidays = fields.Selection([('one', '星期一'), ('two', '星期二'), ('three', '星期三'),
('four', '星期四'), ('five', '星期五'), ('six', '星期六'),
('seven', '星期日')], string='固定休息日')
shift_planning_id = fields.Many2one('yuthon.shift.planning', string='关联排班安排')
class YuthonShiftEverWhiteLine(models.Model):
_name = 'yuthon.shift.ever.white.line'
_description = '常白班'
employee_id = fields.Many2one('hr.employee', string='人员',
domain=lambda self: [('department_id.name', 'in', ['保安部', '班组A', '班组B','班组C'])])
sequence = fields.Integer(string='序号')
regular_holidays = fields.Selection([('one', '星期一'), ('two', '星期二'), ('three', '星期三'),
('four', '星期四'), ('five', '星期五'), ('six', '星期六'),
('seven', '星期日')], string='固定休息日')
shift_planning_id = fields.Many2one('yuthon.shift.planning', string='关联排班安排')
class YuthonShiftEverNightLine(models.Model):
_name = 'yuthon.shift.ever.noon.line'
_description = '常中班'
employee_id = fields.Many2one('hr.employee', string='人员',
domain=lambda self: [('department_id.name', 'in', ['保安部', '班组A', '班组B','班组C'])])
sequence = fields.Integer(string='序号')
regular_holidays = fields.Selection([('one', '星期一'), ('two', '星期二'), ('three', '星期三'),
('four', '星期四'), ('five', '星期五'), ('six', '星期六'),
('seven', '星期日')], string='固定休息日')
shift_planning_id = fields.Many2one('yuthon.shift.planning', string='关联排班安排')