@@ -44,6 +44,7 @@
|
||||
'views/leave_approval.xml',
|
||||
'views/overtime_page_views.xml',
|
||||
'views/remove_holiday_approval.xml',
|
||||
'views/message_reminder_tasks.xml',
|
||||
'views/yuthon_special_approval_views.xml',
|
||||
'views/card_approval.xml',
|
||||
'views/duty_approval.xml',
|
||||
|
||||
@@ -14,5 +14,44 @@
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
</record>
|
||||
<record forcecreate="True" id="message_reminder_tasks_time" model="ir.cron">
|
||||
<field name="name">定时任务:生成消息提醒任务</field>
|
||||
<field name="model_id" ref="model_message_reminder_tasks"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.get_reminder_tasks()</field>
|
||||
<field eval="True" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 00:00:00')"/>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
</record>
|
||||
<record forcecreate="True" id="reminder_card_time" model="ir.cron">
|
||||
<field name="name">定时任务:执行消息提醒任务</field>
|
||||
<field name="model_id" ref="model_hr_attendance"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.reminder_card()</field>
|
||||
<field eval="True" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 00:00:00')"/>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
</record>
|
||||
<record forcecreate="True" id="message_reminder_delete_tasks_time" model="ir.cron">
|
||||
<field name="name">定时任务:删除3天前消息提醒任务</field>
|
||||
<field name="model_id" ref="model_message_reminder_tasks"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.cron_delete_3days_ago_records()</field>
|
||||
<field eval="True" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">days</field>
|
||||
<field name="nextcall" eval="(DateTime.now()).strftime('%Y-%m-%d 00:00:00')"/>
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -31,6 +31,7 @@ from . import expatriation_approval
|
||||
from . import meal_approval
|
||||
from . import back_approval
|
||||
from . import staff_dining_report
|
||||
from . import message_reminder_tasks
|
||||
|
||||
# from odoo.tools import SQL
|
||||
# import odoo
|
||||
|
||||
@@ -63,6 +63,26 @@ class HrAttendance(models.Model):
|
||||
is_last_month = fields.Boolean(string="是否为上月数据", compute='_compute_month_status', store=True)
|
||||
correct_result = fields.Text(string="修正原因")
|
||||
|
||||
@api.constrains("check_in")
|
||||
def _check_in_out(self):
|
||||
reminder_id = self.env['message.reminder.tasks'].search([('name', '=', self.employee_id.name), ('state', '=', 'todo'),
|
||||
('type', '=', 'work'),
|
||||
('current_data', '=', self.current_data)])
|
||||
if reminder_id:
|
||||
reminder_id.state = 'done'
|
||||
|
||||
@api.constrains("check_out")
|
||||
def _check_out_in(self):
|
||||
if self.shift_name in ['夜班', '旧晚(旧区府晚班)']:
|
||||
current_data = self.current_data + timedelta(days=1)
|
||||
else:
|
||||
current_data = self.current_data
|
||||
reminder_id = self.env['message.reminder.tasks'].search([('name', '=', self.employee_id.name), ('state', '=', 'todo'),
|
||||
('type', '=', 'offWork'),
|
||||
('current_data', '=', current_data)])
|
||||
if reminder_id:
|
||||
reminder_id.state = 'done'
|
||||
|
||||
def task_remind(self):
|
||||
all_employees = self.env['hr.employee'].search([('name', 'in', ['Administrator', '叶华锋'])])
|
||||
for employee in all_employees:
|
||||
@@ -1001,6 +1021,21 @@ class HrAttendance(models.Model):
|
||||
content_item=content_item, )
|
||||
|
||||
|
||||
def reminder_card(self):
|
||||
current_date = (fields.Date.today() + relativedelta(hours=8)).date()
|
||||
now = fields.datetime.now() + timedelta(hours=8)
|
||||
reminder_ids = self.env['message.reminder.tasks'].search(
|
||||
[('state', '=', 'todo'), ('current_data', '=', current_date)])
|
||||
for task in reminder_ids:
|
||||
hour1 = (task.reminder_time + timedelta(hours=8)).hour
|
||||
minute1 = (task.reminder_time + timedelta(hours=8)).minute
|
||||
if minute1 == now.minute and hour1 == now.hour:
|
||||
employee_id = self.env['hr.employee'].search([('name', '=', task.name)])
|
||||
if task.type == 'work':
|
||||
self.card_in_comment(employee_id, task.shift_name)
|
||||
else:
|
||||
self.card_out_comment(employee_id, task.shift_name)
|
||||
|
||||
def update_abnormal(self):
|
||||
attendance_ids = self.env["hr.attendance"].browse(
|
||||
self._context.get('active_ids', self._context.get('active_id')))
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
# -*- 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 get_reminder_tasks(self):
|
||||
current_date = (fields.Date.today() + relativedelta(hours=8)).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)
|
||||
])
|
||||
default_reminder_types = ['work', 'offWork']
|
||||
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())
|
||||
]
|
||||
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 if breastfeeding_employees else []
|
||||
# 获取不在白名单且是行政班次的人员
|
||||
final_employees = condition1_employees.filtered(lambda emp: emp not in condition2_employee_ids)
|
||||
task_vals_list = []
|
||||
is_workday = self._process_shift_days(current_date)
|
||||
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)
|
||||
reminder_types = ['work', 'offWork']
|
||||
shift_name = '行政班次(哺乳)'
|
||||
else:
|
||||
reminder_times = default_reminder_times
|
||||
reminder_types = default_reminder_types
|
||||
shift_name = '行政班次'
|
||||
|
||||
for reminder_time, reminder_type in zip(reminder_times, reminder_types):
|
||||
task_vals = {
|
||||
'name': emp_name,
|
||||
'current_data': reminder_time.date(),
|
||||
'department': emp_department,
|
||||
'company': emp_company,
|
||||
'shift_name': shift_name,
|
||||
'type': reminder_type,
|
||||
'reminder_time': fields.Datetime.to_string(reminder_time),
|
||||
}
|
||||
task_vals_list.append(task_vals)
|
||||
|
||||
# 计划行政
|
||||
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:
|
||||
reminder_plan_times = []
|
||||
reminder_plan_types = []
|
||||
planning_slot = self.env['planning.slot'].search([
|
||||
('resource_id.name', '=', employee.name),
|
||||
('start_datetime', '>=', slot_start),
|
||||
('start_datetime', '<', slot_end),
|
||||
], limit=1)
|
||||
template = self.env['planning.slot.template'].search([('planning_name', '=', planning_slot.planning_name)], limit=1)
|
||||
if template:
|
||||
sch_check_in_time, sch_check_out_time = self.normal_clock_in_time(template, current_date)
|
||||
|
||||
is_lactating = employee.id in breastfeeding_employee_ids
|
||||
if is_lactating:
|
||||
reminder_plan_times = self.get_reminder_lactating_personnel(employee.id, current_date)
|
||||
reminder_plan_types = ['work', 'offWork']
|
||||
shift_name = f"{template.planning_name}(哺乳)"
|
||||
else:
|
||||
reminder_plan_times.append(sch_check_in_time - timedelta(minutes=5))
|
||||
reminder_plan_types.append('work')
|
||||
reminder_plan_times.append(sch_check_out_time)
|
||||
reminder_plan_types.append('offWork')
|
||||
shift_name = template.planning_name
|
||||
|
||||
for reminder_time, reminder_type in zip(reminder_plan_times, reminder_plan_types):
|
||||
task_plan_vals = {
|
||||
'name': employee.name,
|
||||
'department': employee.department_id.name,
|
||||
'company': employee.company_id.name,
|
||||
'shift_name': shift_name,
|
||||
'current_data': (reminder_time + relativedelta(hours=8)).date(),
|
||||
'type': reminder_type,
|
||||
'reminder_time': fields.Datetime.to_string(reminder_time),
|
||||
}
|
||||
task_vals_list.append(task_plan_vals)
|
||||
|
||||
self.env['message.reminder.tasks'].create(task_vals_list)
|
||||
|
||||
|
||||
def get_reminder_lactating_personnel(self, employee, current_data):
|
||||
# 查找当天的哺乳假记录(匹配员工+日期范围)
|
||||
breastfeeding_record = self.env['breastfeeding.holiday'].search([
|
||||
('employee_id', '=', employee.id),
|
||||
('calendar_start_time', '<=', current_data),
|
||||
('calendar_end_time', '>=', current_data),
|
||||
], limit=1) # 取当天有效的第一条记录
|
||||
def float_to_datetime(float_time, base_date):
|
||||
"""将float时间(如8.5=8:30)转换为datetime"""
|
||||
if not float_time or not base_date:
|
||||
return None
|
||||
hours = int(float_time)
|
||||
minutes = int((float_time - hours) * 60)
|
||||
return fields.Datetime.to_datetime(f"{base_date} {hours:02d}:{minutes:02d}:00")
|
||||
|
||||
sch_check_in_time = float_to_datetime(breastfeeding_record.check_in, current_data) - timedelta(hours=8)
|
||||
sch_check_out_time = float_to_datetime(breastfeeding_record.check_out, current_data) - timedelta(hours=8)
|
||||
return [
|
||||
sch_check_in_time - timedelta(minutes=5),
|
||||
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):
|
||||
"""
|
||||
判断是否是工作日 调休日 双休
|
||||
"""
|
||||
calendar_leaves = self.env['resource.calendar.leaves'].search([("calendar_id", "=", "行政考勤工作日")])
|
||||
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}日'
|
||||
@@ -78,4 +78,5 @@ access_back_approval,back.approval,model_back_approval,base.group_user,1,1,1,1
|
||||
access_staff_dining_report,staff.dining.report,model_staff_dining_report,base.group_user,1,1,1,1
|
||||
|
||||
access_vacation_confirm_wizard,vacation.confirm.wizard,model_vacation_confirm_wizard,base.group_user,1,1,1,1
|
||||
access_vacation_put_wizard,vacation.put.wizard,model_vacation_put_wizard,base.group_user,1,1,1,1
|
||||
access_vacation_put_wizard,vacation.put.wizard,model_vacation_put_wizard,base.group_user,1,1,1,1
|
||||
access_message_reminder_tasks,message.reminder.tasks,model_message_reminder_tasks,base.group_user,1,1,1,1
|
||||
|
@@ -0,0 +1,32 @@
|
||||
<odoo>
|
||||
<record id="view_message_reminder_tasks_tree" model="ir.ui.view">
|
||||
<field name="name">message.reminder.tasks.tree</field>
|
||||
<field name="model">message.reminder.tasks</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="消息提醒任务" editabile="bottom">
|
||||
<field name="name"/>
|
||||
<field name="department"/>
|
||||
<field name="company"/>
|
||||
<field name="shift_name"/>
|
||||
<field name="type"/>
|
||||
<field name="reminder_time"/>
|
||||
<field name="current_data"/>
|
||||
<field name="state"/>
|
||||
<field name="remake"/>
|
||||
<field name="holiday"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="message_reminder_tasks_action_chief" model="ir.actions.act_window">
|
||||
<field name="name">行政班次任务</field>
|
||||
<field name="res_model">message.reminder.tasks</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('shift_name','in', ['行政班次', '行政班次(哺乳)'])]</field>
|
||||
</record>
|
||||
<record id="message_reminder_tasks_action_plan" model="ir.actions.act_window">
|
||||
<field name="name">计划班次任务</field>
|
||||
<field name="res_model">message.reminder.tasks</field>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[('shift_name','not in', ['行政班次', '行政班次(哺乳)'])]</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -247,26 +247,36 @@
|
||||
parent="attendance_resources_setting_root_board"
|
||||
sequence="2"
|
||||
action="yuthon_base.act_view_ir_cron_action"/>
|
||||
<menuitem id="tasks_action_chief_board"
|
||||
name="行政班次任务"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
sequence="3"
|
||||
action="yuthon_hr_attendance.message_reminder_tasks_action_chief"/>
|
||||
<menuitem id="tasks_action_plan_board"
|
||||
name="计划班次任务"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
sequence="4"
|
||||
action="yuthon_hr_attendance.message_reminder_tasks_action_plan"/>
|
||||
<menuitem id="menu_hr_attendance_work_board"
|
||||
name="考勤时间表"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
sequence="3"
|
||||
sequence="5"
|
||||
action="resource.action_resource_calendar_form"/>
|
||||
<menuitem id="menu_hr_attendance_overtime_page_board"
|
||||
name="加班说明"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
sequence="4"
|
||||
sequence="8"
|
||||
action="yuthon_hr_attendance.view_overtime_page_action"/>
|
||||
<menuitem id="menu_hr_resource_calendar_board"
|
||||
name="法定节假日表"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
sequence="6"
|
||||
sequence="10"
|
||||
action="yuthon_hr_attendance.resource_calendar_leaves_action_from_calendar"/>
|
||||
<menuitem id="holiday_set_type_board2"
|
||||
name="休假类型"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
action="yuthon_holiday.action_leave_type_form"
|
||||
sequence="9"/>
|
||||
sequence="13"/>
|
||||
<menuitem id="holiday_set_scheme_board"
|
||||
name="年假"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
@@ -286,11 +296,11 @@
|
||||
name="假期流程说明"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
action="yuthon_holiday.view_yuthon_holiday_setting_action"
|
||||
sequence="30"/>
|
||||
sequence="50"/>
|
||||
<menuitem id="applet_security_setting_board"
|
||||
name="小程序权限配置"
|
||||
parent="attendance_resources_setting_root_board"
|
||||
sequence="40"
|
||||
sequence="99"
|
||||
action="yuthon_hr_attendance.applet_security_setting_action"/>
|
||||
|
||||
<!-- 薪资管理 -->
|
||||
|
||||
Reference in New Issue
Block a user