添加功能

This commit is contained in:
李鹏宇
2026-09-18 10:59:36 +08:00
parent bb7bb55c15
commit 0fd73d9bad
@@ -96,133 +96,121 @@ class HrAttendanceDetail(models.Model):
}
@api.model
def _get_attendance_template(self, employee, current_data):
"""根据员工与打卡日期获取对应的班次模板,找不到返回 None。
逻辑与 compute_attendance_detail 中一致:先按排班(planning.slot)匹配,
再按部门层级+角色兜底,最后回退到「集团行政」。
"""
slot_start = datetime.combine(current_data, datetime.min.time()) - timedelta(hours=8)
slot_end = datetime.combine(current_data + timedelta(days=1), datetime.min.time()) - timedelta(hours=8)
planning_slots = self.env['planning.slot'].search([
('resource_id', '=', employee.resource_id.id),
('start_datetime', '>=', slot_start),
('start_datetime', '<', slot_end),
], limit=1)
planning_name = planning_slots.planning_name if planning_slots else None
def get_all_upper_depts(dept):
dept_ids = [dept.id]
if dept.parent_id:
dept_ids.extend(get_all_upper_depts(dept.parent_id))
return dept_ids
all_dept_ids = get_all_upper_depts(employee.department_id)
template_domain = []
if planning_name:
template_domain.append(('planning_name', '=', planning_name))
template_domain.append(('department_id', 'in', all_dept_ids))
template = self.env['planning.slot.template'].search(template_domain, limit=1)
# 模板里找不到这个人则置空,继续往下兜底
if template and employee.id not in template.role_id.resource_ids.employee_id.ids:
template = None
if not template:
role_templates = [
{'role_name': '厨卫(集团)', 'template': None},
{'role_name': '项目运营岗', 'template': None}
]
for item in role_templates:
item['template'] = self.env['planning.slot.template'].search(
[('role_id.name', '=', item['role_name'])])
for item in role_templates:
if item['template']:
employee_ids = item['template'].role_id.resource_ids.employee_id.ids or []
if employee.id in list(employee_ids):
template = item['template']
break
if not template:
template = self.env['planning.slot.template'].search(
[('role_id.name', '=', '集团行政')], limit=1)
return template
def _send_att_notify(self, employee_id, message, record, vals, test_users=None):
"""发送企业微信考勤通知并写入 remark 日志。
:param test_users: 命中该名单的员工改用管理员测试号发送(保留原调试逻辑)
"""
test_users = test_users or []
if employee_id.name in test_users:
self.sudo().env["wecom.apps"].sync_send_message(
category='att', user_id='18562027762',
message=message + f'管理员测试管理员测试{vals}',
)
else:
self.sudo().env["wecom.apps"].sync_send_message(
category='att', user_id=employee_id.wecom_userid,
message=message,
)
record.write({'remark': message})
_logger.info(message)
def create(self, vals):
record = super(HrAttendanceDetail, self).create(vals)
# 批量导入时跳过企业微信通知
if self.env.context.get('skip_iot_notify'):
return record
if record.employee_id.name not in ['Administrator', '张丽芳']:
resource_user_ids = record.employee_id.resource_calendar_id.resource_user_ids.ids
is_resource_user = True if record.employee_id.id in resource_user_ids else False
employee_id = record.employee_id
checkin_datetime = record.checkin_time
checkin_datetime = record.checkin_time # UTC 时间
resource_user_ids = employee_id.resource_calendar_id.resource_user_ids.ids
is_resource_user = employee_id.id in resource_user_ids
# 上一条记录存在且时间差 < 60秒 → 不重复提醒
last_record = self.search([
('employee_id', '=', employee_id.id),
('source', '=', 'iot'),
('checkin_time', '<', checkin_datetime)
], order='checkin_time desc', limit=1)
# 如果上一条记录存在,并且时间差 < 60秒 → 不发提醒
skip_notify = False
if last_record:
time_diff = checkin_datetime - last_record.checkin_time
if time_diff.total_seconds() < 60:
skip_notify = True # 小于1分钟,跳过提醒
if record.employee_id and record.checkin_time and record.source == 'iot' and not is_resource_user and not skip_notify:
current_data = record.checkin_time.date() + timedelta(hours=8)
day_start = checkin_datetime.replace(hour=0, minute=0, second=0, microsecond=0)
day_end = checkin_datetime.replace(hour=23, minute=59, second=59, microsecond=99)
daily_iot_records = self.search([
('employee_id', '=', employee_id.id),
('source', '=', 'iot'),
('checkin_time', '>=', day_start),
('checkin_time', '<=', day_end)
], order='checkin_time asc')
if daily_iot_records:
message = f"打卡成功!今日上班时间:{record.checkin_time + timedelta(hours=8)}: {employee_id.name},祝您工作愉快!"
self.sudo().env["wecom.apps"].sync_send_message(
category='att', user_id=employee_id.wecom_userid,
message=message,
)
# if record.employee_id.name == '蒋彬':
# self.sudo().env["wecom.apps"].sync_send_message(
# category='att', user_id='18562027762',
# message=message + f'管理员测试管理员测试{vals}',
# )
self.write({'remark': message})
_logger.info(message)
skip_notify = bool(last_record) and (checkin_datetime - last_record.checkin_time).total_seconds() < 60
if employee_id and checkin_datetime and record.source == 'iot' and not is_resource_user and not skip_notify:
# 本地时间表示(UTC+8),用于匹配班次模板与标准上下班时间
current_data = checkin_datetime.date() + timedelta(hours=8)
template = self._get_attendance_template(employee_id, current_data)
if not template:
# 找不到班次模板,默认按上班打卡提醒
message = f"打卡成功!今日上班时间:{checkin_datetime + timedelta(hours=8)}: {employee_id.name},祝您工作愉快!"
self._send_att_notify(employee_id, message, record, vals)
else:
resource_user_ids = employee_id.resource_calendar_id.resource_user_ids.ids
is_resource_user = True if employee_id.id in resource_user_ids else False
# 哺乳人员
sch_check_in_time, sch_check_out_time = self.normal_clock_in_time(template, current_data)
# 哺乳人员:下午班次下班时间提前 1 小时
suckling_employees = employee_id.resource_calendar_id.breastfeeding_holiday_ids.mapped('employee_id')
def get_all_upper_depts(dept):
"""递归获取部门及其所有上级部门的ID列表"""
dept_ids = [dept.id]
if dept.parent_id:
dept_ids.extend(get_all_upper_depts(dept.parent_id))
return dept_ids
all_dept_ids = get_all_upper_depts(employee_id.department_id)
if not is_resource_user:
slot_start = datetime.combine(current_data, datetime.min.time()) - timedelta(hours=8)
slot_end = datetime.combine(current_data + timedelta(days=1), datetime.min.time()) - timedelta(
hours=8)
# 查找员工对应的班次
planning_slots = self.env['planning.slot'].search([
('resource_id', '=', employee_id.resource_id.id),
('start_datetime', '>=', slot_start),
('start_datetime', '<', slot_end),
], limit=1)
planning_name = planning_slots.planning_name if planning_slots else None
template_domain = []
if planning_name:
template_domain.append(('planning_name', '=', planning_name))
template_domain.append(('department_id', 'in', all_dept_ids))
template = self.env['planning.slot.template'].search(template_domain, limit=1)
# 如果模板里面找不到这个人 则还是往下判断(模板为空)
if employee_id.id not in template.role_id.resource_ids.employee_id.ids:
template = None
if not template:
role_templates = [
{'role_name': '厨卫(集团)', 'template': None},
{'role_name': '项目运营岗', 'template': None}
]
for item in role_templates:
item['template'] = self.env['planning.slot.template'].search(
[('role_id.name', '=', item['role_name'])], )
for item in role_templates:
if item['template']:
employee_ids = item['template'].role_id.resource_ids.employee_id.ids or []
employee_list = list(employee_ids)
if employee_id.id in employee_list:
template = item['template']
break
if not template:
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_data)
# 判断是否是哺乳人员
if employee_id in suckling_employees:
# 判断是否是哺乳人员并调整标准打卡时间
current_date = fields.Date.to_date(current_data) if current_data else None
if current_date:
# 查找当天的哺乳假记录(匹配员工+日期范围)
breastfeeding_record = self.env['breastfeeding.holiday'].search([
('employee_id', '=', employee_id.id),
('calendar_start_time', '<=', current_date),
('calendar_end_time', '>=', current_date),
], limit=1)
if breastfeeding_record.time_slot_selection == 'afternoon':
sch_check_out_time = sch_check_out_time - timedelta(hours=1)
if checkin_datetime > sch_check_out_time:
message = f"打卡成功!今日下班时间:{record.checkin_time + timedelta(hours=8)}: {employee_id.name},祝您归途平安!"
if record.employee_id.name not in ['林伟东']:
self.sudo().env["wecom.apps"].sync_send_message(
category='att', user_id=employee_id.wecom_userid,
message=message,
)
else:
self.sudo().env["wecom.apps"].sync_send_message(
category='att', user_id='18562027762',
message=message + f'管理员测试管理员测试{vals}',
)
self.write({'remark': message})
_logger.info(message)
if employee_id in suckling_employees:
current_date = fields.Date.to_date(current_data)
if current_date:
breastfeeding_record = self.env['breastfeeding.holiday'].search([
('employee_id', '=', employee_id.id),
('calendar_start_time', '<=', current_date),
('calendar_end_time', '>=', current_date),
], limit=1)
if breastfeeding_record.time_slot_selection == 'afternoon':
sch_check_out_time = sch_check_out_time - timedelta(hours=1)
# 以标准下班时间为界:晚于下班时间算「下班」,否则算「上班」
if checkin_datetime > sch_check_out_time:
message = f"打卡成功!今日下班时间:{checkin_datetime + timedelta(hours=8)}: {employee_id.name},祝您归途平安!"
self._send_att_notify(employee_id, message, record, vals, test_users=['林伟东'])
else:
message = f"打卡成功!今日上班时间:{checkin_datetime + timedelta(hours=8)}: {employee_id.name},祝您工作愉快!"
self._send_att_notify(employee_id, message, record, vals)
if record.employee_id and record.checkin_time:
self._sync_to_hr_attendance(record)