@@ -96,111 +96,107 @@ class HrAttendanceDetail(models.Model):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def _get_attendance_template(self, employee, current_data):
|
||||||
record = super(HrAttendanceDetail, self).create(vals)
|
"""根据员工与打卡日期获取对应的班次模板,找不到返回 None。
|
||||||
# 批量导入时跳过企业微信通知
|
|
||||||
if self.env.context.get('skip_iot_notify'):
|
逻辑与 compute_attendance_detail 中一致:先按排班(planning.slot)匹配,
|
||||||
return record
|
再按部门层级+角色兜底,最后回退到「集团行政」。
|
||||||
if record.employee_id.name not in ['Administrator', '张丽芳']:
|
"""
|
||||||
resource_user_ids = record.employee_id.resource_calendar_id.resource_user_ids.ids
|
slot_start = datetime.combine(current_data, datetime.min.time()) - timedelta(hours=8)
|
||||||
is_resource_user = True if record.employee_id.id in resource_user_ids else False
|
slot_end = datetime.combine(current_data + timedelta(days=1), datetime.min.time()) - timedelta(hours=8)
|
||||||
employee_id = record.employee_id
|
planning_slots = self.env['planning.slot'].search([
|
||||||
checkin_datetime = record.checkin_time
|
('resource_id', '=', employee.resource_id.id),
|
||||||
last_record = self.search([
|
('start_datetime', '>=', slot_start),
|
||||||
('employee_id', '=', employee_id.id),
|
('start_datetime', '<', slot_end),
|
||||||
('source', '=', 'iot'),
|
], limit=1)
|
||||||
('checkin_time', '<', checkin_datetime)
|
planning_name = planning_slots.planning_name if planning_slots else None
|
||||||
], 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)
|
|
||||||
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
|
|
||||||
# 哺乳人员
|
|
||||||
suckling_employees = employee_id.resource_calendar_id.breastfeeding_holiday_ids.mapped('employee_id')
|
|
||||||
def get_all_upper_depts(dept):
|
def get_all_upper_depts(dept):
|
||||||
"""递归获取部门及其所有上级部门的ID列表"""
|
|
||||||
dept_ids = [dept.id]
|
dept_ids = [dept.id]
|
||||||
if dept.parent_id:
|
if dept.parent_id:
|
||||||
dept_ids.extend(get_all_upper_depts(dept.parent_id))
|
dept_ids.extend(get_all_upper_depts(dept.parent_id))
|
||||||
return dept_ids
|
return dept_ids
|
||||||
|
|
||||||
all_dept_ids = get_all_upper_depts(employee_id.department_id)
|
all_dept_ids = get_all_upper_depts(employee.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 = []
|
template_domain = []
|
||||||
if planning_name:
|
if planning_name:
|
||||||
template_domain.append(('planning_name', '=', planning_name))
|
template_domain.append(('planning_name', '=', planning_name))
|
||||||
template_domain.append(('department_id', 'in', all_dept_ids))
|
template_domain.append(('department_id', 'in', all_dept_ids))
|
||||||
template = self.env['planning.slot.template'].search(template_domain, limit=1)
|
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:
|
if template and employee.id not in template.role_id.resource_ids.employee_id.ids:
|
||||||
template = None
|
template = None
|
||||||
if not template:
|
if not template:
|
||||||
role_templates = [
|
role_templates = [
|
||||||
{'role_name': '厨卫(集团)', 'template': None},
|
{'role_name': '厨卫(集团)', 'template': None},
|
||||||
{'role_name': '项目运营岗', 'template': None}
|
{'role_name': '项目运营岗', 'template': None}
|
||||||
]
|
]
|
||||||
|
|
||||||
for item in role_templates:
|
for item in role_templates:
|
||||||
item['template'] = self.env['planning.slot.template'].search(
|
item['template'] = self.env['planning.slot.template'].search(
|
||||||
[('role_id.name', '=', item['role_name'])], )
|
[('role_id.name', '=', item['role_name'])])
|
||||||
for item in role_templates:
|
for item in role_templates:
|
||||||
if item['template']:
|
if item['template']:
|
||||||
employee_ids = item['template'].role_id.resource_ids.employee_id.ids or []
|
employee_ids = item['template'].role_id.resource_ids.employee_id.ids or []
|
||||||
employee_list = list(employee_ids)
|
if employee.id in list(employee_ids):
|
||||||
if employee_id.id in employee_list:
|
|
||||||
template = item['template']
|
template = item['template']
|
||||||
break
|
break
|
||||||
if not template:
|
if not template:
|
||||||
template = self.env['planning.slot.template'].search(
|
template = self.env['planning.slot.template'].search(
|
||||||
[('role_id.name', '=', '集团行政')],
|
[('role_id.name', '=', '集团行政')], limit=1)
|
||||||
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', '张丽芳']:
|
||||||
|
employee_id = record.employee_id
|
||||||
|
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)
|
||||||
|
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:
|
||||||
sch_check_in_time, sch_check_out_time = self.normal_clock_in_time(template, current_data)
|
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')
|
||||||
if employee_id in suckling_employees:
|
if employee_id in suckling_employees:
|
||||||
# 判断是否是哺乳人员并调整标准打卡时间
|
current_date = fields.Date.to_date(current_data)
|
||||||
current_date = fields.Date.to_date(current_data) if current_data else None
|
|
||||||
if current_date:
|
if current_date:
|
||||||
# 查找当天的哺乳假记录(匹配员工+日期范围)
|
|
||||||
breastfeeding_record = self.env['breastfeeding.holiday'].search([
|
breastfeeding_record = self.env['breastfeeding.holiday'].search([
|
||||||
('employee_id', '=', employee_id.id),
|
('employee_id', '=', employee_id.id),
|
||||||
('calendar_start_time', '<=', current_date),
|
('calendar_start_time', '<=', current_date),
|
||||||
@@ -208,21 +204,13 @@ class HrAttendanceDetail(models.Model):
|
|||||||
], limit=1)
|
], limit=1)
|
||||||
if breastfeeding_record.time_slot_selection == 'afternoon':
|
if breastfeeding_record.time_slot_selection == 'afternoon':
|
||||||
sch_check_out_time = sch_check_out_time - timedelta(hours=1)
|
sch_check_out_time = sch_check_out_time - timedelta(hours=1)
|
||||||
|
# 以标准下班时间为界:晚于下班时间算「下班」,否则算「上班」
|
||||||
if checkin_datetime > sch_check_out_time:
|
if checkin_datetime > sch_check_out_time:
|
||||||
message = f"打卡成功!今日下班时间:{record.checkin_time + timedelta(hours=8)}: {employee_id.name},祝您归途平安!"
|
message = f"打卡成功!今日下班时间:{checkin_datetime + timedelta(hours=8)}: {employee_id.name},祝您归途平安!"
|
||||||
if record.employee_id.name not in ['林伟东']:
|
self._send_att_notify(employee_id, message, record, vals, test_users=['林伟东'])
|
||||||
self.sudo().env["wecom.apps"].sync_send_message(
|
|
||||||
category='att', user_id=employee_id.wecom_userid,
|
|
||||||
message=message,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.sudo().env["wecom.apps"].sync_send_message(
|
message = f"打卡成功!今日上班时间:{checkin_datetime + timedelta(hours=8)}: {employee_id.name},祝您工作愉快!"
|
||||||
category='att', user_id='18562027762',
|
self._send_att_notify(employee_id, message, record, vals)
|
||||||
message=message + f'管理员测试管理员测试{vals}',
|
|
||||||
)
|
|
||||||
self.write({'remark': message})
|
|
||||||
_logger.info(message)
|
|
||||||
|
|
||||||
if record.employee_id and record.checkin_time:
|
if record.employee_id and record.checkin_time:
|
||||||
self._sync_to_hr_attendance(record)
|
self._sync_to_hr_attendance(record)
|
||||||
|
|||||||
Reference in New Issue
Block a user