309 lines
13 KiB
Python
309 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models, tools, _
|
|
from datetime import datetime, time, timedelta
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class MessageReminderTasks(models.Model):
|
|
_name = 'message.reminder.tasks'
|
|
_description = '消息提醒任务'
|
|
|
|
name = fields.Char(string="姓名")
|
|
department = fields.Char(string="部门")
|
|
company = fields.Char(string="公司")
|
|
shift_name = fields.Char(string="班次")
|
|
current_data = fields.Date(string='打卡日期')
|
|
state = fields.Selection([('todo', '待提醒'), ('done', '已提醒')], string='状态', default='todo')
|
|
type = fields.Selection([('work', '上班'), ('offWork', '下班')], string='类型')
|
|
reminder_time = fields.Datetime(string="提醒时间")
|
|
remake = fields.Char(string="备注")
|
|
holiday = fields.Boolean(string="假期")
|
|
|
|
def remove_duplicate_reminder_tasks(self):
|
|
tasks = self.env['message.reminder.tasks'].search([])
|
|
from collections import defaultdict
|
|
task_groups = defaultdict(list)
|
|
for task in tasks:
|
|
key = (task.name, task.current_data, task.type)
|
|
task_groups[key].append(task)
|
|
for key, task_list in task_groups.items():
|
|
if len(task_list) <= 1:
|
|
continue # 无重复,跳过
|
|
done_tasks = [t for t in task_list if t.state == 'done']
|
|
keep_task = None
|
|
if done_tasks:
|
|
keep_task = done_tasks[0]
|
|
else:
|
|
keep_task = task_list[0]
|
|
for task in task_list:
|
|
if task != keep_task:
|
|
task.unlink()
|
|
|
|
def get_reminder_tasks(self):
|
|
"""每天自动生成当日提醒任务"""
|
|
current_date = (fields.Date.today() + relativedelta(hours=8)).date()
|
|
self._generate_reminder_tasks_by_date(current_date)
|
|
|
|
def generate_weekly_reminder_tasks(self):
|
|
"""批量生成 2026-04-15 至 2026-04-21 提醒任务"""
|
|
start_date = fields.Date.from_string('2026-04-15')
|
|
end_date = fields.Date.from_string('2026-04-21')
|
|
|
|
current_date = start_date
|
|
while current_date <= end_date:
|
|
self._generate_reminder_tasks_by_date(current_date)
|
|
current_date += timedelta(days=1)
|
|
|
|
def _generate_reminder_tasks_by_date(self, current_date):
|
|
"""按指定日期生成提醒任务(每日/批量共用逻辑)"""
|
|
target_company_names = ['天河物管', '天河粮储', '集团本部']
|
|
target_companies = self.env['res.company'].search([('name', 'in', target_company_names)])
|
|
target_calendars = self.env['resource.calendar'].search([
|
|
('company_id', 'in', target_companies.ids)
|
|
])
|
|
|
|
# 日历白名单员工
|
|
whitelist_employees = target_calendars.mapped('resource_user_ids').ids
|
|
condition1_employees = self.env['hr.employee'].search([
|
|
('id', 'not in', whitelist_employees),
|
|
('name', '!=', 'Administrator'),
|
|
('user_state', '=', 'in'),
|
|
('active', '=', True)
|
|
])
|
|
|
|
# 角色:保安、工程、联安粮库
|
|
target_role_names = ['保安部', '工程部', '联安粮库']
|
|
target_roles = self.env['planning.role'].search([('name', 'in', target_role_names)])
|
|
|
|
condition2_employee_ids = []
|
|
if target_roles:
|
|
for role in target_roles:
|
|
resource_employees = role.resource_ids.mapped('employee_id')
|
|
condition2_employee_ids.extend(resource_employees)
|
|
condition2_employee_ids = list(set(condition2_employee_ids))
|
|
|
|
# 哺乳人员
|
|
breastfeeding_employees = target_calendars.mapped('breastfeeding_holiday_ids.employee_id')
|
|
breastfeeding_employee_ids = breastfeeding_employees.ids or []
|
|
|
|
# 行政班人员(不在白名单、不在角色排班)
|
|
final_employees = condition1_employees.filtered(lambda emp: emp not in condition2_employee_ids)
|
|
|
|
task_vals_list = []
|
|
is_workday = self._process_shift_days(current_date)
|
|
|
|
# 集团行政模板
|
|
template = self.env['planning.slot.template'].search([('role_id.name', '=', '集团行政')], limit=1)
|
|
sch_check_in_time, sch_check_out_time = self.normal_clock_in_time(template, current_date)
|
|
|
|
# 默认提醒时间
|
|
default_reminder_times = [
|
|
datetime.combine(current_date, datetime.strptime('00:55', '%H:%M').time()),
|
|
datetime.combine(current_date, datetime.strptime('10:00', '%H:%M').time())
|
|
]
|
|
default_reminder_types = ['work', 'offWork']
|
|
|
|
# 1. 行政班次
|
|
if is_workday:
|
|
for employee in final_employees:
|
|
emp_name = employee.name
|
|
emp_department = employee.department_id.name
|
|
emp_company = employee.company_id.name
|
|
is_lactating = employee.id in breastfeeding_employee_ids
|
|
|
|
if is_lactating:
|
|
reminder_times = self.get_reminder_lactating_personnel(employee, current_date, sch_check_in_time,
|
|
sch_check_out_time)
|
|
reminder_types = ['work', 'offWork']
|
|
shift_name = '行政班次(哺乳)'
|
|
else:
|
|
reminder_times = default_reminder_times
|
|
reminder_types = default_reminder_types
|
|
shift_name = '行政班次'
|
|
|
|
for rt, rtype in zip(reminder_times, reminder_types):
|
|
current_data_val = rt.date()
|
|
|
|
# ================== 加了这里:判断是否已存在 ==================
|
|
exists = self.env['message.reminder.tasks'].search([
|
|
('name', '=', emp_name),
|
|
('current_data', '=', current_data_val),
|
|
('type', '=', rtype),
|
|
], limit=1)
|
|
if exists:
|
|
continue # 已存在,跳过不创建
|
|
# ==============================================================
|
|
|
|
task_vals = {
|
|
'name': emp_name,
|
|
'current_data': current_data_val,
|
|
'department': emp_department,
|
|
'company': emp_company,
|
|
'shift_name': shift_name,
|
|
'type': rtype,
|
|
'reminder_time': fields.Datetime.to_string(rt),
|
|
}
|
|
task_vals_list.append(task_vals)
|
|
|
|
# 2. 计划排班班次
|
|
slot_start = datetime.combine(current_date, datetime.min.time()) - timedelta(hours=8)
|
|
slot_end = datetime.combine(current_date + timedelta(days=1), datetime.min.time()) - timedelta(hours=8)
|
|
|
|
for employee in condition2_employee_ids:
|
|
planning_slot = self.env['planning.slot'].search([
|
|
('resource_id.name', '=', employee.name),
|
|
('start_datetime', '>=', slot_start),
|
|
('start_datetime', '<', slot_end),
|
|
], limit=1)
|
|
|
|
if not planning_slot:
|
|
continue
|
|
|
|
template = self.env['planning.slot.template'].search([
|
|
('planning_name', '=', planning_slot.planning_name)
|
|
], limit=1)
|
|
|
|
if not template:
|
|
continue
|
|
|
|
c_in, c_out = self.normal_clock_in_time(template, current_date)
|
|
is_lactating = employee.id in breastfeeding_employee_ids
|
|
|
|
if is_lactating:
|
|
r_times = self.get_reminder_lactating_personnel(employee, current_date, c_in, c_out)
|
|
r_types = ['work', 'offWork']
|
|
shift_name = f"{template.planning_name}(哺乳)"
|
|
else:
|
|
r_times = [c_in - timedelta(minutes=5), c_out]
|
|
r_types = ['work', 'offWork']
|
|
shift_name = template.planning_name
|
|
|
|
for rt, rtype in zip(r_times, r_types):
|
|
current_data_val = (rt + timedelta(hours=8)).date()
|
|
|
|
exists = self.env['message.reminder.tasks'].search([
|
|
('name', '=', employee.name),
|
|
('current_data', '=', current_data_val),
|
|
('type', '=', rtype),
|
|
], limit=1)
|
|
if exists:
|
|
continue # 已存在,跳过不创建
|
|
|
|
task_plan_vals = {
|
|
'name': employee.name,
|
|
'department': employee.department_id.name,
|
|
'company': employee.company_id.name,
|
|
'shift_name': shift_name,
|
|
'current_data': current_data_val,
|
|
'type': rtype,
|
|
'reminder_time': fields.Datetime.to_string(rt),
|
|
}
|
|
task_vals_list.append(task_plan_vals)
|
|
|
|
# 创建数据
|
|
if task_vals_list:
|
|
self.env['message.reminder.tasks'].create(task_vals_list)
|
|
|
|
|
|
def get_reminder_lactating_personnel(self, employee, current_data, sch_check_in_time, sch_check_out_time):
|
|
# 查找当天的哺乳假记录(匹配员工+日期范围)
|
|
breastfeeding_record = self.env['breastfeeding.holiday'].search([
|
|
('employee_id', '=', employee.id),
|
|
('calendar_start_time', '<=', current_data),
|
|
('calendar_end_time', '>=', current_data),
|
|
], limit=1) # 取当天有效的第一条记录
|
|
if breastfeeding_record:
|
|
if breastfeeding_record.time_slot_selection == 'morning':
|
|
sch_check_in_time = sch_check_in_time + timedelta(hours=1)
|
|
else:
|
|
sch_check_out_time = sch_check_out_time - timedelta(hours=1)
|
|
return [
|
|
sch_check_in_time,
|
|
sch_check_out_time
|
|
]
|
|
|
|
def normal_clock_in_time(self, template, current_data):
|
|
"""
|
|
:param template: 班次模板
|
|
:param current_data: 打卡日期
|
|
:return: 正常开始上班时间和下班时间
|
|
"""
|
|
if template.day2 == '次日':
|
|
check_out_end = template.end_time2
|
|
else:
|
|
check_out_end = template.end_time
|
|
check_in_start = template.start_time
|
|
date = current_data
|
|
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)
|
|
start_datetime = datetime.combine(date, datetime.min.time()).replace(
|
|
hour=start_hour, minute=start_minute, second=0, microsecond=0
|
|
) - timedelta(hours=8)
|
|
if template.day2 == '次日':
|
|
end_datetime = datetime.combine(date, datetime.min.time()).replace(
|
|
hour=end_hour, minute=end_minute, second=0, microsecond=0
|
|
) - timedelta(hours=8) + timedelta(days=1)
|
|
else:
|
|
end_datetime = datetime.combine(date, datetime.min.time()).replace(
|
|
hour=end_hour, minute=end_minute, second=0, microsecond=0
|
|
) - timedelta(hours=8)
|
|
|
|
if template.planning_name == '中夜':
|
|
end_datetime = datetime.combine(date, datetime.min.time()).replace(
|
|
hour=22, minute=0, second=0, microsecond=0
|
|
) - timedelta(hours=8)
|
|
|
|
return start_datetime, end_datetime
|
|
|
|
def cron_delete_3days_ago_records(self):
|
|
three_days_ago = fields.Datetime.now() - timedelta(days=3)
|
|
old_records = self.search([
|
|
('create_date', '<=', three_days_ago)
|
|
])
|
|
if old_records:
|
|
old_records.unlink()
|
|
|
|
def _process_shift_days(self, current_data):
|
|
"""
|
|
判断是否是工作日 调休日 双休
|
|
"""
|
|
current_year = (fields.Date.today() + timedelta(hours=8)).year
|
|
query_start = datetime(current_year, 1, 1, 0, 0, 0) - timedelta(hours=8)
|
|
query_end = datetime(current_year, 12, 31, 23, 59, 59) - timedelta(hours=8)
|
|
calendar_leaves = self.env['resource.calendar.leaves'].sudo().search([
|
|
("calendar_id.name", "=", "行政考勤工作日"),
|
|
("date_from", ">=", query_start),
|
|
("date_from", "<=", query_end),
|
|
('resource_id', '=', False),
|
|
])
|
|
holidays_list = []
|
|
shift_days_list = []
|
|
# 提取节假日和调休日
|
|
for leave in calendar_leaves:
|
|
if leave.holidays:
|
|
holidays_list.extend(leave.holidays.split(','))
|
|
if leave.alternate_rest_day:
|
|
holidays_list.extend(leave.alternate_rest_day.split(','))
|
|
if leave.shift_replacement_day:
|
|
shift_days_list.extend(leave.shift_replacement_day.split(','))
|
|
|
|
holidays_list = [self.remove_leading_zero(date) for date in holidays_list]
|
|
shift_days_list = [self.remove_leading_zero(date) for date in shift_days_list]
|
|
result = f"{current_data.month}月{current_data.day}日"
|
|
if result in shift_days_list:
|
|
return True
|
|
elif (result in holidays_list) or (current_data.weekday() in [5, 6]):
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def remove_leading_zero(self, date_str):
|
|
month, day = date_str.split('月')
|
|
month = month.lstrip('0')
|
|
day = day.rstrip('日')
|
|
day = day.lstrip('0')
|
|
return f'{month}月{day}日' |