Merge pull request !9073 from 李鹏宇/dev_wang
This commit is contained in:
李鹏宇
2026-02-28 03:37:55 +00:00
committed by Gitee
4 changed files with 161 additions and 6 deletions
+1
View File
@@ -16,6 +16,7 @@
'views/yuthon_catering_setting_views.xml',
'views/yuthon_catering_templates.xml',
'wizard/catering_reinstated_wizard_views.xml',
'data/yuthon_catering_cron.xml',
],
'assets': {
'web.assets_backend': [
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data noupdate="1">
<record forcecreate="True" id="catering_reminder_cron" model="ir.cron">
<field name="name">定时任务:餐饮登记提醒</field>
<field name="model_id" ref="model_yuthon_catering"/>
<field name="state">code</field>
<field name="code">model.cron_send_message()</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 09:30:00')"/>
<field name="numbercall">-1</field>
<field eval="False" name="doall"/>
</record>
</data>
</odoo>
+137 -5
View File
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models
from datetime import datetime, timedelta
@@ -23,23 +23,32 @@ class YuthonCatering(models.Model):
conf_records = self.env['yuthon.catering.conf'].search([])
employee_ids = conf_records.mapped('employee_ids')
# 获取今天的日期
today = fields.Date.today()
# 创建未来7天的日期列表
date_list = [today + timedelta(days=i) for i in range(7)]
# 初始化待创建记录列表
create_vals = []
# 遍历每个员工
for employee in employee_ids:
# 遍历每个日期
for target_date in date_list:
# 检查目标日期是否为工作日,如果不是则跳过
if not self._process_shift_days(target_date):
continue
# 检查是否已存在该员工在该日期的记录
existing = self.search([
('employee_id', '=', employee.id),
('date', '=', target_date)
], limit=1)
# 查找当天的菜单设置
menu_record = self.env['yuthon.menu.setting'].search([
('menu_date', '=', target_date)
], limit=1)
# 如果不存在记录,则准备创建新记录的数据
if not existing:
create_vals.append({
'menu_record': menu_record.id if menu_record else False,
@@ -47,52 +56,175 @@ class YuthonCatering(models.Model):
'employee_id': employee.id,
})
# 如果有待创建的记录,则批量创建
if create_vals:
self.create(create_vals)
# 定义私有方法,用于判断给定日期是否为工作日
def _process_shift_days(self, current_data):
"""
判断是否是工作日 调休日 双休
"""
# 获取当前年份
current_year = (fields.Date.today() + timedelta(hours=8)).year
# 设置查询开始时间(当年1月1日)
query_start = datetime(current_year, 1, 1, 0, 0, 0) - timedelta(hours=8)
# 设置查询结束时间(当年12月31日)
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)
])
# 初始化假期列表
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}日"
# 如果日期在调休列表中,返回True(是工作日)
if result in shift_days_list:
return True
# 如果日期在假期列表中或为周末,返回False(不是工作日)
elif (result in holidays_list) or (current_data.weekday() in [5, 6]):
return False
# 其他情况返回True(是工作日)
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}日'
# 定义定时任务方法,每天九点半发送消息提醒
def cron_send_message(self):
# 登记页面
url = f'/catering/registration/<int:employee_id>'
# 统计页面
url = f'/catering/statistics/<int:employee_id>'
"""
定时任务:每天上午九点半发送消息提醒
"""
# 获取当前时间(本地时间)
current_utc_time = fields.Datetime.now()
local_time = current_utc_time + timedelta(hours=8)
h = local_time.hour
m = local_time.minute
# 检查是否是上午9:30左右
if not (h == 9 and 25 <= m <= 35): # 9:25-9:35之间
return
# 判断今天是否为工作日
today = fields.Date.today()
if not self._process_shift_days(today):
# 如果不是工作日,则不发送提醒
return
# 发送通用提醒消息
employees = self.env['hr.employee'].search([('user_id', '!=', False)])
for employee in employees:
if employee.user_id: # 确保员工有关联的用户
# 构造跳转URL,格式为 f'https://phone.thtzjt.com/phone/Restaurant/(users_id)
users_id = employee.user_id.id
target_url = f'https://phone.thtzjt.com/phone/Restaurant/{users_id}'
# 发送消息,使用指定的格式
if hasattr(self.env["wecom.apps"], 'sync_send_message_textcard'):
description = f"您有1条待办未处理,请及时登OA系统处理,谢谢配合"
title = "餐饮登记提醒"
self.env["wecom.apps"].sync_send_message_textcard(
category='oa',
user_id=employee.wecom_userid if hasattr(employee, 'wecom_userid') else employee.user_id.login,
title=title,
description=description,
url=target_url,
btntxt="查看详情"
)
# 定义批量提醒方法
def batch_remind_catering(self):
"""
批量提醒功能:手动触发提醒选定的记录
"""
# 从环境上下文中获取选中的记录ID
active_ids = self.env.context.get('active_ids')
# 浏览选中的记录,如果没有则使用当前记录
selected_records = self.browse(active_ids) if active_ids else self
# 如果没有选中任何记录,抛出异常
if not selected_records:
raise ValueError("没有选择任何记录进行提醒")
# 按员工分组存储待通知的记录
employees_to_notify = {}
# 遍历选中的记录
for record in selected_records:
# 只处理员工存在且状态为未确认的记录
if record.employee_id and record.state == 'unconfirmed':
# 如果员工ID不在字典中,则初始化
if record.employee_id.id not in employees_to_notify:
employees_to_notify[record.employee_id.id] = {
'employee': record.employee_id,
'records': []
}
# 将记录添加到对应员工的列表中
employees_to_notify[record.employee_id.id]['records'].append(record)
# 发送提醒给每个员工
notified_count = 0
# 遍历员工字典
for emp_id, data in employees_to_notify.items():
employee = data['employee']
records = data['records']
# 构造跳转URL
users_id = employee.user_id.id if employee.user_id else ''
target_url = f'https://phone.thtzjt.com/phone/Restaurant/{users_id}'
# 发送消息,使用指定的格式
if hasattr(self.env["wecom.apps"], 'sync_send_message_textcard'):
# 构造消息内容
description = f"您有{len(data['records'])}条待办未处理,请及时登OA系统处理,谢谢配合"
# 设置消息标题
title = "餐饮登记提醒"
# 调用企业微信API发送消息卡片
self.env["wecom.apps"].sync_send_message_textcard(
category='oa', # 消息类别为OA
# 获取员工的企业微信ID或登录名
user_id=employee.wecom_userid if hasattr(employee, 'wecom_userid') else employee.user_id.login if employee.user_id else '',
title=title, # 消息标题
description=description, # 消息内容
url=target_url, # 点击后跳转的URL
btntxt="查看详情" # 按钮文字
)
# 增加已通知计数
notified_count += 1
# 返回通知结果
return {
'type': 'ir.actions.client', # 动作类型为客户端动作
'tag': 'display_notification', # 标签为显示通知
'params': {
'title': '批量提醒', # 通知标题
'message': f'已向 {notified_count} 名员工发送提醒消息', # 通知内容
'sticky': False, # 不是粘性通知
}
}
+5 -1
View File
@@ -7,13 +7,16 @@
<field name="model">yuthon.catering</field>
<field name="arch" type="xml">
<tree string="餐饮" editable="top">
<header>
<button name="batch_remind_catering" string="批量提醒" type="object" class="btn btn-primary"/>
</header>
<field name="date"/>
<field name="dept_id"/>
<field name="employee_id"/>
<field name="breakfast"/>
<field name="lunch"/>
<field name="amount"/>
<field name="menu_day_id"/>
<field name="menu_day_id" column_invisible="True"/>
<field name="note"/>
</tree>
</field>
@@ -24,6 +27,7 @@
<field name="name">餐饮收集任务</field>
<field name="res_model">yuthon.catering</field>
<field name="view_mode">tree,form</field>
<field name="context">{'default_order': 'date desc'}</field>
</record>