298 lines
13 KiB
Python
298 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
||
from odoo import api, fields, models, tools, _
|
||
from odoo.exceptions import ValidationError, UserError
|
||
import logging
|
||
from datetime import datetime, timedelta, date
|
||
|
||
_logger = logging.getLogger(__name__)
|
||
|
||
|
||
class YuthonNotice(models.Model):
|
||
_name = "yuthon.notice"
|
||
_description = '通知公告'
|
||
_inherit = ['soong.workflow.mixin', 'mail.thread.main.attachment', 'mail.activity.mixin']
|
||
_rec_name = 'name'
|
||
_order = "sequence desc"
|
||
|
||
name = fields.Char(string='标题')
|
||
code = fields.Char(string="编号", default=lambda self: self.env['ir.sequence'].next_by_code('notice_code'))
|
||
users_id = fields.Many2one('res.users', string="发布人", default=lambda self: self.env.user)
|
||
company_id = fields.Many2one(related="users_id.employee_id.company_id", string="公司", store=True)
|
||
department_id = fields.Many2one(related='users_id.employee_id.department_id', string="部门")
|
||
notice_type_id = fields.Many2one('yuthon.notice.type', string="类型")
|
||
create_date1 = fields.Date(string="发布日期", default=fields.Date.today())
|
||
start_date = fields.Date(string="生效日期", default=fields.Date.today())
|
||
end_date = fields.Date(string="终止日期")
|
||
sequence = fields.Integer(string="顺序", default=1, compute='_compute_sequence')
|
||
employee_ids = fields.Many2many('hr.employee', string="人员范围")
|
||
department_ids = fields.Many2many('hr.department', string='部门范围')
|
||
users_role_ids = fields.Many2many('soong.user.role', string='角色范围')
|
||
users_role_group_ids = fields.Many2many('soong.user.role.group', string='角色组')
|
||
notice_state = fields.Selection([('no', '未发布'), ('yes', '已发布'), ('stop', '已终止')], default='no', string="公告状态")
|
||
documents_ids = fields.Many2many('ir.attachment', string="附件")
|
||
is_notice_roger = fields.Selection([('yes', '已读'), ('no', '未读')], default='no', string="确认状态")
|
||
is_change_notice = fields.Boolean(string="是否发送通知", default=True)
|
||
attn_notice = fields.Html(string="经办人意见")
|
||
notice_text = fields.Html(string="正文")
|
||
notice_text1 = fields.Html(string="正文")
|
||
read_number = fields.Integer(string='阅读量', compute="_compute_read_number", store=True)
|
||
is_this_company = fields.Boolean(string='本公司', default=True)
|
||
approval_user_id = fields.Many2one('res.users', string="审核人")
|
||
approval_date = fields.Datetime(string="审核日期")
|
||
is_sticky = fields.Boolean(string="是否置顶")
|
||
urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度', default='normal')
|
||
active = fields.Boolean('Active', default=True, tracking=True)
|
||
|
||
work_end = fields.Boolean(string="结束")
|
||
users_ids = fields.Many2many(
|
||
'res.users',
|
||
relation='yuthon_notice_users_rel',
|
||
column1='notice_id',
|
||
column2='user_id',
|
||
string='用户'
|
||
)
|
||
self_users_ids = fields.Many2many(
|
||
'res.users',
|
||
relation='yuthon_notice_current_users_rel',
|
||
column1='notice_id',
|
||
column2='user_id',
|
||
string="当前经办人",
|
||
default=lambda self: [(6, 0, [self.env.user.id])]
|
||
)
|
||
|
||
# @api.onchange('users_ids')
|
||
# def _onchange_users_ids(self):
|
||
# today = date.today()
|
||
# self.start_date = today
|
||
# self.create_date1 = today
|
||
|
||
is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company',
|
||
search='_search_part_of_company')
|
||
|
||
publish_date = fields.Date(string='发布日期')
|
||
|
||
@api.depends_context('uid', 'company')
|
||
@api.depends('company_id')
|
||
def _compute_is_company(self):
|
||
active_company_ids = self.env.companies
|
||
for employee in self:
|
||
employee.is_company = employee.company_id in active_company_ids
|
||
|
||
def _search_part_of_company(self, operator, value):
|
||
company_ids = self.env.companies.ids
|
||
if not value:
|
||
operator = '!=' if operator == '=' else '='
|
||
if operator == '=':
|
||
return [('company_id', 'in', company_ids)]
|
||
else:
|
||
return [('company_id', 'not in', company_ids)]
|
||
|
||
@api.model_create_multi
|
||
def create(self, vals_list):
|
||
for i in vals_list:
|
||
if i['department_ids'] == i['employee_ids'] == i['users_role_ids'] == i['users_role_group_ids']:
|
||
raise UserError('请填写发布的范围')
|
||
return super().create(vals_list)
|
||
|
||
def _get_default_name_title(self):
|
||
return self._description if self._description else self._name
|
||
|
||
name_title = fields.Char(default=_get_default_name_title)
|
||
|
||
@api.constrains('employee_ids', 'department_ids', 'users_role_ids', 'users_role_group_ids')
|
||
def _constrains_employee_related_fields(self):
|
||
"""
|
||
约束验证:当关联字段变化时,更新users_ids
|
||
"""
|
||
def get_department_employees(department_id):
|
||
"""递归获取部门及其子部门的员工对应的user_id"""
|
||
valid_user_ids = set()
|
||
# 查找当前部门的员工(使用id替代id.origin)
|
||
employee_ids = self.env['hr.employee'].search([('department_id', '=', department_id.id)])
|
||
# 收集有效的user_id
|
||
valid_user_ids.update([emp.user_id.id for emp in employee_ids if emp.user_id])
|
||
# 递归处理子部门
|
||
child_ids = self.env['hr.department'].search([('parent_id', '=', department_id.id)])
|
||
for child in child_ids:
|
||
valid_user_ids.update(get_department_employees(child))
|
||
return valid_user_ids
|
||
|
||
for notice in self:
|
||
# 避免重复触发约束(更新users_ids时跳过本次约束检查)
|
||
if notice.env.context.get('skip_constrains'):
|
||
continue
|
||
# 获取部门关联的用户ID
|
||
department_user_ids = set()
|
||
for department_id in notice.department_ids:
|
||
department_user_ids.update(get_department_employees(department_id))
|
||
group_role_ids = notice.users_role_group_ids.user_role_ids
|
||
all_users = set(
|
||
notice.employee_ids.mapped('user_id').ids +
|
||
group_role_ids.mapped('personnel_ids.user_id').ids +
|
||
notice.users_role_ids.mapped('personnel_ids.user_id').ids +
|
||
list(department_user_ids)
|
||
)
|
||
notice.with_context(skip_constrains=True).users_ids = [(6, 0, list(all_users))]
|
||
|
||
if notice.users_ids and notice.is_change_notice:
|
||
conf_old = self.env['yuthon.confirm.users'].sudo().search([('notice_id', '=', notice.id)])
|
||
old_employee_ids = conf_old.mapped('employee_id.id')
|
||
new_employee_ids = self.env['hr.employee'].sudo().search([
|
||
('user_id', 'in', notice.users_ids.ids)
|
||
]).ids
|
||
add_employee_ids = list(set(new_employee_ids) - set(old_employee_ids))
|
||
del_employee_ids = list(set(old_employee_ids) - set(new_employee_ids))
|
||
|
||
# 3. 处理删除:
|
||
if del_employee_ids:
|
||
del_conf = conf_old.filtered(lambda c: c.employee_id.id in del_employee_ids)
|
||
del_conf.unlink()
|
||
|
||
if add_employee_ids:
|
||
all_employees = self.env['hr.employee'].sudo().search([
|
||
('user_id', 'in', self.users_ids.ids)
|
||
])
|
||
for employee in all_employees:
|
||
self.env['yuthon.confirm.users'].create({
|
||
'notice_id': self.id,
|
||
'employee_id': employee.id,
|
||
'notice_code': self.code,
|
||
'notice_name': self.name,
|
||
'release_date': self.create_date1,
|
||
'state': 'to_sent',
|
||
})
|
||
|
||
def get_no_read_count(self):
|
||
return self.search_count([('is_notice_roger', '=', 'no'), ('notice_state', '=', 'yes')])
|
||
|
||
def read(self, fields=None, load='_classic_read'):
|
||
for con in self:
|
||
con_id = self.env['yuthon.confirm.users'].search([('notice_id', '=', con.id), ('employee_id', '=', con.env.user.employee_id.id)])
|
||
if con_id.employee_id == con.env.user.employee_id:
|
||
con.is_notice_roger = 'yes'
|
||
con_id.write({'state': 'yes'})
|
||
return super(YuthonNotice, self).read(fields=fields, load=load)
|
||
|
||
@api.depends('read_number')
|
||
def _compute_read_number(self):
|
||
notice_confirm_ids = self.env['yuthon.confirm.users'].search(
|
||
[('notice_id.id', '=', self.id), ('state', '=', 'yes')])
|
||
for read in self:
|
||
if notice_confirm_ids:
|
||
read.read_number = len(notice_confirm_ids)
|
||
else:
|
||
read.read_number = 0
|
||
|
||
def on_sequence(self):
|
||
self.sequence = 1
|
||
|
||
@api.onchange('is_sticky')
|
||
def _compute_sequence(self):
|
||
for i in self:
|
||
if i.is_sticky:
|
||
i.sequence = 1
|
||
else:
|
||
i.sequence = 0
|
||
|
||
def no_sequence(self):
|
||
self.sequence = 0
|
||
|
||
def notice_publish(self):
|
||
self.ensure_one()
|
||
|
||
# 幂等性保护:如果已经发布,不再执行
|
||
if self.notice_state == 'yes':
|
||
_logger.info(f"公告 {self.name} (ID: {self.id}) 已发布,跳过重复发布")
|
||
return
|
||
|
||
self.notice_state = 'yes'
|
||
if self.is_change_notice:
|
||
conf_old = self.env['yuthon.confirm.users'].sudo().search([
|
||
('notice_id', '=', self.id),
|
||
('employee_id.user_id', 'in', self.users_ids.ids)
|
||
])
|
||
if conf_old:
|
||
conf_old.write({'state': 'no'})
|
||
user_wecom_mapping = []
|
||
# 对用户列表去重,防止重复发送
|
||
unique_user_ids = list(set(self.users_ids.ids))
|
||
_logger.info(f"公告 {self.name} (ID: {self.id}) 准备发送给 {len(unique_user_ids)} 个用户")
|
||
|
||
# 查询已存在的确认记录,用于去重
|
||
existing_confirm = self.env['yuthon.confirm.users'].sudo().search([
|
||
('notice_id', '=', self.id),
|
||
('notice_code', '=', self.code),
|
||
])
|
||
existing_emp_ids = set(existing_confirm.mapped('employee_id.id'))
|
||
|
||
for user_id in unique_user_ids:
|
||
employee = self.env['hr.employee'].sudo().search([('user_id', '=', user_id)], limit=1)
|
||
if not employee:
|
||
continue
|
||
|
||
# 方案B:用户级别去重——已存在确认记录的,不再重复发送
|
||
if employee.id in existing_emp_ids:
|
||
_logger.info(f"用户 {employee.name} 的确认记录已存在,跳过发送")
|
||
continue
|
||
|
||
url = f'https://phone.thtzjt.com/phone/Notice/{self.id}?employee_id={employee.id}'
|
||
result_data = self.sudo().env["wecom.apps"].sync_send_message_textcard(
|
||
category='notice',
|
||
touser=employee.wecom_userid,
|
||
title="通知公告",
|
||
description=f"{self.name} 发布人: {self.users_id.name if self.users_id else ''}",
|
||
url=url,
|
||
btntxt="详细信息"
|
||
)
|
||
msg_id = None
|
||
if result_data:
|
||
res_dict = result_data[1]
|
||
msg_id = res_dict.get('msgid')
|
||
user_wecom_mapping.append({
|
||
'emp_id': employee.id,
|
||
'msg_id': msg_id
|
||
})
|
||
for mapping in user_wecom_mapping:
|
||
emp_id = mapping['emp_id']
|
||
msg_id = mapping['msg_id']
|
||
self.env['yuthon.confirm.users'].sudo().create({
|
||
'notice_id': self.id,
|
||
'employee_id': emp_id,
|
||
'notice_code': self.code,
|
||
'notice_name': self.name,
|
||
'release_date': self.create_date1,
|
||
'state': 'no',
|
||
'msg_id': msg_id,
|
||
})
|
||
|
||
def look_up_notice_users(self):
|
||
return {
|
||
"type": "ir.actions.act_window",
|
||
"name": "通知公告确认信息",
|
||
"domain": [("notice_id", "=", self.id)],
|
||
"res_model": "yuthon.confirm.users",
|
||
"view_mode": "tree",
|
||
"context": {
|
||
"search_default_group_state": True,
|
||
},
|
||
}
|
||
|
||
def stop_notice(self):
|
||
self.write({
|
||
'notice_state': 'stop',
|
||
'end_date': fields.Date.today(),
|
||
})
|
||
|
||
def action_print_notice(self):
|
||
"""打印通知公告"""
|
||
self.ensure_one()
|
||
report = self.env.ref('yuthon_notice.action_notice_print_report')
|
||
return report.report_action(self)
|
||
|
||
|
||
class YuthonCsTree(models.Model):
|
||
_name = 'yuthon.cs.tree'
|
||
_description = 'Yuthon Customer Service Tree'
|
||
|
||
name = fields.Char(string='Name', required=True)
|