# -*- coding: utf-8 -*- import re from email.policy import default from odoo import api, fields, models, tools from odoo.exceptions import UserError, ValidationError from datetime import datetime, timedelta, date class IssueLibrary(models.Model): _name = "issue.library" _description = '议题总库' _inherit = ['mail.thread.main.attachment', 'mail.activity.mixin'] _rec_name = 'name' name = fields.Char(string='议题名称') name_text = fields.Text(string='议题名称', compute='_compute_name', readonly=False, store=True) depart_opinions = fields.Text(string='部门意见') topic_content = fields.Text(string='事项背景') will_based = fields.Text(string='上会依据') deliberation_content = fields.Text(string='请示事项', default='建议同意') handling_situation = fields.Text(string='办理情况', help="(存在问题)") is_invisible_workflow = fields.Boolean(string="流程隐藏的判断") code = fields.Char(string='编号', required=True, copy=False, default=lambda self: self.env['ir.sequence'].next_by_code('issue_library_code')) its_original_state = fields.Char(string='原议题状态') supporting_department = fields.Text(string='协办部门/单位') topic_type = fields.Selection([('scrutiny', '审议'), ('learn', '学习'), ('report', '汇报')], string='议题类型', default='scrutiny') issue_state = fields.Selection([ ('to_do', '待推送'), ('draft', '待申报'), ('to_draft', '申报中'), ('to_approval', '待上会审批'), ('approval', '上会审批中'), ('on_approval', '已上会审批'), ('cancel', '已取消'), ('to_modify', '待修改') ], default='to_do', string="议题状态") is_confirm = fields.Boolean(string="确认") original_issue_state = fields.Selection([ ('to_do', '待推送'), ('draft', '待申报'), ('to_draft', '申报中'), ('to_approval', '待上会审批'), ('approval', '上会审批中'), ('on_approval', '已上会审批'), ('cancel', '已取消') ], string="原始议题状态", help="存储切换到待修改前的原始状态,用于修改完成后还原") issue_state_party = fields.Selection([('',''), ('will', '待上会'), ('meeting', '上会中'), ('on_meet', '已上会')], default='will', string="会议状态") issue_state_class = fields.Selection([('',''), ('will', '待上会'), ('meeting', '上会中'), ('on_meet', '已上会')], default='will', string="会议状态") issue_state_boss = fields.Selection([('',''), ('will', '待上会'), ('meeting', '上会中'), ('on_meet', '已上会')], default='will', string="会议状态") is_party = fields.Boolean(string="党总支", store=True) party_char = fields.Char(string=' ') is_class = fields.Boolean(string="班子会", store=True) class_char = fields.Char(string=' ') is_boss = fields.Boolean(string="董事会", store=True) boss_char = fields.Char(string=' ') is_note = fields.Boolean(string="上级单位",store=True) is_district_state = fields.Boolean(string="区国资局", store=True) is_district_government = fields.Boolean(string="区政府", store=True) is_district_party = fields.Boolean(string="区委组织部", store=True) creart_datetime = fields.Date(string="创建日期", default=fields.Datetime.now) documents_ids = fields.Many2many('ir.attachment', string='议题附件') issue_type_id = fields.Many2one('issue.type', string="议题类型") relevancy_type = fields.Selection([('none', '无')], string="关联流程类型") issue_approval_id = fields.Many2one('issue.approval', string="关联议题审批") issue_library_line_ids = fields.One2many('issue.library.line', 'issue_library_id', string="决策明细") issue_library_approval_line_ids = fields.One2many('issue.library.approval.line', 'issue_library_id', string='流程记录') avoid_line_ids = fields.One2many('issue.library.avoid.line', 'issue_library_id', string='回避记录') approval_decision_sequence = fields.Char(string="决策清单事项") is_booking = fields.Boolean(string="是否上会") issues_result = fields.Html(string="会议结果") cs_party = fields.Char(string='党支部会') cs_class = fields.Char(string='班子会') cs_boss = fields.Char(string='董事会') issue_library_room_line_ids = fields.One2many('issue.library.room.line', 'issue_library_id', string="关联会议明细行") # 权限相关隐藏字段 employee_id = fields.Many2one('hr.employee', string='创建人', default=lambda self: self.env.user.employee_id) department_id = fields.Many2one('hr.department', string='部门', related='employee_id.department_id') users_role_ids = fields.Many2many('soong.user.role', string='角色范围', related='employee_id.user_role_ids') company_id = fields.Many2one('res.company', string='公司', related='employee_id.company_id', store=True) is_legal_trial = fields.Selection([('no', '否'), ('yes', '是')], string="是否法审", default='no') co_organizer_dept = fields.Char(string="协办部门/单位") workflow_id = fields.Many2one('soong.workflow.record', string="流程") department_personnel_ids = fields.Many2many('res.users', string='部门人员') is_external_person = fields.Selection([('no', '否'), ('yes', '是')], string="外部人员参会", default='no') is_admin = fields.Boolean(string='是否管理员', compute='_compute_is_admin') issue_attendee_line_ids = fields.One2many('issue.library.attendee.line', 'issue_library_id', string='列席人员信息') issue_external_person_line_ids = fields.One2many('issue.library.external.person.line', 'issue_library_id', string='外部参会人员') approval_name = fields.Char(string="审批标题") meeting_place = fields.Char(string="会议地点") meeting_name = fields.Char(string="会议名称") active = fields.Boolean('Active', default=True, tracking=True) urgency_level = fields.Selection([('normal', '普件'), ('urgent', '急件'), ('emergency', '特急件')], string='紧急度', default='normal') work_end = fields.Boolean(string="结束") member_of_department = fields.Boolean("是否是本部门数据", compute='_compute_part_of_department', search='_search_part_of_department') is_modify = fields.Boolean(string="是否修改") is_party_passed = fields.Selection([('',''), ('pass', '通过'), ('reject', '未通过')]) is_class_passed = fields.Selection([('',''), ('pass', '通过'), ('reject', '未通过')]) is_boss_passed = fields.Selection([('',''), ('pass', '通过'), ('reject', '未通过')]) push_invisible = fields.Boolean(string="推送隐藏") is_push_party = fields.Boolean(string="党推送") is_push_class = fields.Boolean(string="班子推送") is_push_boss = fields.Boolean(string="董事推送") is_push_depart = fields.Boolean(string="部门推送") is_meeting = fields.Boolean(string="已过会", compute='_compute_meeting', store=True) is_company = fields.Boolean(string="本公司", default=True, compute='_compute_is_company', search='_search_part_of_company') @api.depends_context('uid', 'company') @api.depends('company_id') def _compute_is_company(self): active_company_ids = self.env.companies for record in self: record.is_company = record.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)] def log_avoidance(self, avoid_user, avoid_comment=''): """记录回避信息,已存在的回避人不重复创建""" existing = self.env['issue.library.avoid.line'].search([ ('issue_library_id', '=', self.id), ('avoid_user_id', '=', avoid_user.id), ], limit=1) if not existing: self.env['issue.library.avoid.line'].create({ 'issue_library_id': self.id, 'avoid_user_id': avoid_user.id, 'avoid_date': fields.Datetime.now(), 'avoid_comment': avoid_comment, }) def correction_confirm(self): for rec in self: rec.is_confirm = False @api.depends('is_party', 'is_party_passed', 'is_class', 'is_class_passed', 'is_boss', 'is_boss_passed') def _compute_meeting(self): for rec in self: rec.is_meeting = False enabled = [] rec.is_party and enabled.append(rec.is_party_passed == 'pass') rec.is_class and enabled.append(rec.is_class_passed == 'pass') rec.is_boss and enabled.append(rec.is_boss_passed == 'pass') if enabled and all(enabled): rec.is_meeting = True def all_issue_library_line_ids(self): issue_active_ids = self.env["issue.library"].browse( self._context.get('active_ids', self._context.get('active_id')) ) for record in issue_active_ids: record._compute_meeting() def batch_add_ci_char(self): import re active_ids = self._context.get('active_ids', []) records = self.env['issue.library'].browse(active_ids) count = 0 for record in records: # 清理party_char的HTML标签 if record.party_char: clean_value = re.sub(r'<[^>]+>', '', record.party_char) if clean_value != record.party_char: record.party_char = clean_value count += 1 # 清理class_char的HTML标签 if record.class_char: clean_value = re.sub(r'<[^>]+>', '', record.class_char) if clean_value != record.class_char: record.class_char = clean_value count += 1 return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': '批量更新完成', 'message': f'成功更新 {count} 条记录', 'sticky': False, } } def meeting_record(self): self.ensure_one() domain = [ ('id', 'in', self.env['issue.meeting.line'].search([ ('issue_library_id', '=', self.id) ]).mapped('meeting_booking_id.id')) ] return { 'name': '会议详情', 'type': 'ir.actions.act_window', 'view_mode': 'tree,form', 'res_model': 'room.booking', 'domain': domain, 'target': 'current', } def action_is_modify(self): for rec in self: rec.issue_state = rec.original_issue_state rec.original_issue_state = False def issue_party_approval(self): issue_active_ids = self.env["issue.library"].browse( self._context.get('active_ids', self._context.get('active_id')) ) line_list = [] for record in issue_active_ids: if record.issue_state != 'to_approval': raise UserError("该议题状态不能审批") line_list.append((0, 0, { 'issue_id': record.id, 'name': record.name, 'will_based': record.will_based, 'department_id': record.department_id.id, })) return { 'type': 'ir.actions.act_window', 'name': '党议题审批', "target": "current", 'res_model': 'issue.party.approval', 'view_mode': 'form', 'context': { 'default_state':'申请人', 'default_name_title': '党总支议题审批', 'default_line_ids': line_list, } } def issue_party1_approval(self): issue_active_ids = self.env["issue.library"].browse( self._context.get('active_ids', self._context.get('active_id')) ) line_list = [] for record in issue_active_ids: if record.issue_state != 'to_approval': raise UserError("该议题状态不能审批") line_list.append((0, 0, { 'issue_id': record.id, 'name': record.name, 'will_based': record.will_based, 'department_id': record.department_id.id, })) return { 'type': 'ir.actions.act_window', 'name': '党议题审批', "target": "current", 'res_model': 'issue.party.approval', 'view_mode': 'form', 'context': { 'default_state':'申请人', 'default_name_title': '党支部议题审批', 'default_line_ids': line_list, } } def refunded(self): for rec in self: refund_update_vals = {} if rec.is_party and (rec.is_party_passed == 'reject' or not rec.is_party_passed): refund_update_vals['is_push_party'] = False elif rec.is_class and (rec.is_class_passed == 'reject' or not rec.is_class_passed): refund_update_vals['is_push_class'] = False elif rec.is_boss and (rec.is_boss_passed == 'reject' or not rec.is_boss_passed): refund_update_vals['is_push_boss'] = False if refund_update_vals: rec.push_invisible = False rec.write(refund_update_vals) def issue_class_approval(self): issue_active_ids = self.env["issue.library"].browse( self._context.get('active_ids', self._context.get('active_id')) ) line_list = [] for record in issue_active_ids: if record.issue_state != 'to_approval': raise UserError("该议题状态不能审批") line_list.append((0, 0, { 'issue_id': record.id, 'name': record.name, 'will_based': record.will_based, 'department_id': record.department_id.id, })) return { 'type': 'ir.actions.act_window', 'name': '班子议题审批', "target": "current", 'res_model': 'issue.class.approval', 'view_mode': 'form', 'context': { 'default_state': '申请人', 'default_line_ids': line_list, } } def issue_boss_approval(self): issue_active_ids = self.env["issue.library"].browse( self._context.get('active_ids', self._context.get('active_id')) ) line_list = [] for record in issue_active_ids: if record.issue_state != 'to_approval': raise UserError("该议题状态不能审批") line_list.append((0, 0, { 'issue_id': record.id, 'name': record.name, 'will_based': record.will_based, 'department_id': record.department_id.id, })) return { 'type': 'ir.actions.act_window', 'name': '董事议题审批', "target": "current", 'res_model': 'issue.boss.approval', 'view_mode': 'form', 'context': { 'default_state': '申请人', 'default_line_ids': line_list, } } @api.onchange('issue_library_line_ids', 'issue_library_line_ids.issue_approval_decision_id', 'issue_library_line_ids.district_state_id', 'issue_library_line_ids.district_government_id', 'issue_library_line_ids.district_party_id') def _onchange_will_based(self): for record in self: will_based_lines = [] # 遍历明细行,保留原有逻辑,仅调整触发方式 for idx, line in enumerate(record.issue_library_line_ids, start=1): # 获取各字段值,无值则为空字符串 decision_name = line.issue_approval_decision_id.specifics or "" district_state_name = line.district_state_id.name or "" district_government_name = line.district_government_id.name or "" district_party_name = line.district_party_id.name or "" field_parts = [] # 按规则拼接字段内容 if decision_name: field_parts.append(decision_name) if district_state_name: field_parts.append(f"区国资局({district_state_name})") if district_government_name: field_parts.append(f"区政府({district_government_name})") if district_party_name: field_parts.append(f"区委组织部({district_party_name})") if field_parts: line_content = f"{idx}.{','.join(field_parts)}" will_based_lines.append(line_content) record.will_based = "\n".join(will_based_lines) def correction(self): for rec in self: menu_id = self.env['ir.ui.menu'].search([('name', '=', '我的议题')]) url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=yuthon.notice&view_type=list&menu_id={menu_id.id}' self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', user_id=rec.employee_id.wecom_userid, title="议题修正", description="您有待修正的议题,请尽快修正", url=url, btntxt="详细信息") rec.write({ 'original_issue_state': rec.issue_state, 'issue_state': 'to_modify', 'is_confirm': True, }) def depart_push(self): for rec in self: rec.is_push_depart = True rec.issue_state = 'draft' def library_push(self): for rec in self: push_update_vals = {} if rec.is_party and (rec.is_party_passed == 'reject' or not rec.is_party_passed): push_update_vals['is_push_party'] = True push_update_vals['issue_state_party'] = False elif rec.is_class and (rec.is_class_passed == 'reject' or not rec.is_class_passed): push_update_vals['is_push_class'] = True push_update_vals['issue_state_class'] = False elif rec.is_boss and (rec.is_boss_passed == 'reject' or not rec.is_boss_passed): push_update_vals['is_push_boss'] = True push_update_vals['issue_state_boss'] = False if push_update_vals: rec.push_invisible = True rec.write(push_update_vals) def _compute_is_admin(self): is_admin = self.env.user.has_group('base.group_system') for rec in self: rec.is_admin = is_admin @api.onchange('is_legal_trial') def _onchange_is_legal_trial(self): self.department_personnel_ids = [(5, 0, 0)] if self.is_legal_trial: if self.department_id: target_department_ids = self.env['hr.department'].search([ ('id', 'child_of', self.department_id.id) ]).ids target_employees = self.env['hr.employee'].search([ ('department_id', 'in', target_department_ids), ('user_state', '=', 'in') ]) self.department_personnel_ids = [(6, 0, target_employees.user_id.ids)] else: self.department_personnel_ids = [(5, 0, 0)] def add_workflow(self): return { 'name': '流程添加弹窗', 'type': 'ir.actions.act_window', 'view_mode': 'form', 'res_model': 'issue.line.add.wizard', "target": "new", 'context': { 'default_issue_id': self.id, }, } @api.depends_context('uid', 'company') @api.depends('department_id') def _compute_part_of_department(self): is_manager = self.env.user.has_group('soong_workflow.group_soong_workflow_manager') if is_manager: # 管理员可以看到所有数据,全部标记为 True for employee in self: employee.member_of_department = True return user_employee = self._get_valid_employee_for_user() active_department = user_employee.department_id if active_department: department_ids = self.env['hr.department'].search([ ('id', 'child_of', active_department.id) ]).ids else: department_ids = [] for employee in self: employee.member_of_department = employee.department_id.id in department_ids def _get_valid_employee_for_user(self): user = self.env.user employee = user.employee_ids if not employee: user_employees = user.employee_id.sudo().search([ ('user_id', '=', user.id) ]) employee = user_employees.filtered(lambda r: r.company_id == user.company_id) or user_employees[:1] return employee def _search_part_of_department(self, operator, value): # 检查当前用户是否属于管理员群组 is_manager = self.env.user.has_group('soong_workflow.group_soong_workflow_manager') if is_manager: # 管理员搜索时返回所有数据(domain 为空) if (operator == '=' and value) or (operator == '!=' and not value): return [] # 如果搜索条件是 False,则返回空数据 elif (operator == '=' and not value) or (operator == '!=' and value): return [('id', '=', False)] user_employee = self._get_valid_employee_for_user() active_department = user_employee.department_id if active_department: base_domain = [('department_id', 'child_of', active_department.id)] else: base_domain = [('department_id', '=', False)] return (['!'] if operator == '!=' else []) + base_domain def _get_default_name_title(self): return '我的议题' name_title = fields.Char(default=_get_default_name_title) def read_number(self): return self.search_count([ ('issue_state', '=', 'draft'), ('employee_id.user_id', '=', self.env.user.id) ]) @api.onchange('issue_library_line_ids') def onchange_approval_decision_sequence(self): for dec in self: specifics_list = [] for line in dec.issue_library_line_ids: if line.issue_approval_decision_id.specifics: specifics_list.append(line.issue_approval_decision_id.specifics) dec.approval_decision_sequence = ';'.join(specifics_list) if specifics_list else '' def revoke(self): self.write({'is_push_depart': False, 'issue_state': 'to_do'}) if self.issue_approval_id: issue_line_records = self.env['issue.approval.line'].search([ ('issue_approval_id', '=', self.issue_approval_id.id) ]) if issue_line_records: issue_line_records.unlink() workflow_id = self.env['soong.workflow.record'].search([('res_model', '=', 'issue.approval'), ('res_id', '=', self.issue_approval_id.id)], limit=1) if workflow_id: workflow_id.sudo().unlink() self.issue_approval_id.unlink() def all_issue_pull(self): # 1. 获取选中的议题库记录 issue_active_ids = self.env["issue.library"].browse( self._context.get('active_ids', self._context.get('active_id')) ) # 校验选中记录的状态(非草稿不允许提交) for i in issue_active_ids: if i.issue_state != 'draft': raise UserError("当前状态不允许提交,请先推送") # 2. 查找已存在的关联 issue.approval 记录 existing_approval = self.env['issue.approval'].search([ ('issue_approval_ids.issue_library_id', 'in', issue_active_ids.ids), ], limit=1) # 3. 拼接审批单名称 specifics_list = [rec.name for rec in issue_active_ids] name = ','.join(specifics_list) if len(specifics_list) > 1 else ( self.name or specifics_list[0] if specifics_list else "") if existing_approval: issue_approval_id = existing_approval # 获取已有审批单中已关联的议题库 ID existing_issue_ids = issue_approval_id.issue_approval_ids.mapped('issue_library_id').ids # 筛选出选中但未关联的议题库记录 new_issue_records = issue_active_ids.filtered(lambda r: r.id not in existing_issue_ids) if new_issue_records: new_lines = [] for issue_record in new_issue_records: new_lines.append((0, 0, { 'issue_library_id': issue_record.id, 'approval_decision_sequence': issue_record.approval_decision_sequence, 'employee_id': issue_record.employee_id.id })) # 追加新行到已有审批单 issue_approval_id.write({ 'issue_approval_ids': new_lines, 'name': name }) self.issue_approval_id = issue_approval_id else: issue_decision_lines = [] for issue_record in issue_active_ids: issue_decision_lines.append((0, 0, { 'issue_library_id': issue_record.id, 'approval_decision_sequence': issue_record.approval_decision_sequence, 'employee_id': issue_record.employee_id.id })) issue_approval_id = self.env['issue.approval'].create({ 'issue_approval_ids': issue_decision_lines, 'name': name, }) self.issue_approval_id = issue_approval_id form_id = self.env.ref("yuthon_issue.issue_library_approval_form").id return { "type": "ir.actions.act_window", "name": "提交议题申报", "res_model": "issue.approval", "views": [[form_id, "form"]], "res_id": issue_approval_id.id, "context": { "id": issue_approval_id.id, "is_issue": True, } } def single_issue_pull(self): if self.issue_state != 'draft': raise UserError("当前状态不允许提交") form_id = self.env.ref("yuthon_issue.issue_library_approval_form").id issue_decision_lines = [(0, 0, { 'issue_library_id': self.id, 'approval_decision_sequence': self.approval_decision_sequence, 'employee_id': self.employee_id.id })] issue_approval_id = self.env['issue.approval'].create({ 'issue_approval_ids': issue_decision_lines, 'name': self.name, 'employee_id': self.employee_id.id, }) self.write({"issue_state": "approval"}) self.issue_approval_id = issue_approval_id return { "type": "ir.actions.act_window", "name": "提交审批", "res_model": "issue.approval", "views": [[form_id, "form"]], "res_id": issue_approval_id.id, "context": { "id": issue_approval_id.id, "is_issue": True, } } def issue_cancel(self): self.write({"issue_state": "cancel"}) def all_meeting_issue(self): issue_active_ids = self.env["issue.library"].browse( self._context.get('active_ids', self._context.get('active_id', []))) current_user = self.env.user user_company_name = current_user.company_id.name if current_user.company_id else "" action_id = self._context.get('from_action') for ras in issue_active_ids: issue_details = [] target_meeting_type_id = False meeting_priority = [ ('is_party', 'is_party_passed', None), ('is_class', 'is_class_passed', '班子'), ('is_boss', 'is_boss_passed', '董事'), ] def get_all_sub_departments(department): dept_ids = [department.id] sub_depts = self.env['hr.department'].search([('parent_id', '=', department.id)]) for sub_dept in sub_depts: dept_ids += get_all_sub_departments(sub_dept) return dept_ids all_dept_ids = get_all_sub_departments(ras.department_id) target_employees = self.env['hr.employee'].search([ ('department_id', 'in', all_dept_ids), ('active', '=', True) ]) # 先匹配出最终会议类型(原有逻辑) for issue in issue_active_ids: if target_meeting_type_id: continue # 按「党→班子→董事」优先级匹配 for idx, (field_flag, field_passed, keyword) in enumerate(meeting_priority): if not getattr(issue, field_flag, False): continue passed_result = getattr(issue, field_passed, '') if passed_result in (False, 'reject'): # 班子上会:党的 party_char 不能为空 if idx == 1 and ras.is_party == True: clean_party = re.sub(r'<[^>]+>', '', issue.party_char or '').strip() if not clean_party: raise UserError("党的意见为空,不能上班子会") # 董事上会:党的 party_char 和班子的 class_char 都不能为空 elif idx == 2 and ras.is_party == True and ras.is_class == True: clean_party = re.sub(r'<[^>]+>', '', issue.party_char or '').strip() clean_class = re.sub(r'<[^>]+>', '', issue.class_char or '').strip() if not clean_party: raise UserError("党的意见为空,不能上董事会") if not clean_class: raise UserError("班子意见为空,不能上董事会") if idx == 0: party_meeting_name = "党总支" if user_company_name == "集团本部" else "党支委" meeting_type = self.env['room.meeting.type'].search([('name', 'ilike', party_meeting_name)], limit=1) else: meeting_type = self.env['room.meeting.type'].search([('name', 'ilike', keyword)], limit=1) if meeting_type: target_meeting_type_id = meeting_type.id break elif passed_result == 'pass': continue # 天河粮储 套会逻辑 if len(issue_active_ids) == 2 and all(ras.company_id.name == "天河粮储" for ras in issue_active_ids): record_a, record_b = issue_active_ids condition = ((getattr(record_a, 'is_party', False) and not getattr(record_b, 'is_party', False) and getattr(record_b, 'is_class', False)) or (getattr(record_b, 'is_party', False) and not getattr(record_a, 'is_party', False) and getattr(record_a, 'is_class', False))) all_char_empty = ( not getattr(record_a, 'party_char', '') and not getattr(record_a, 'class_char', '') and not getattr(record_b, 'party_char', '') and not getattr(record_b, 'class_char', '')) if condition and all_char_empty: meeting_type_with_tao = self.env['room.meeting.type'].search([('name', 'ilike', '套')], limit=1) if meeting_type_with_tao: target_meeting_type_id = meeting_type_with_tao.id # ====================== 核心新增:判断会议名称是否含「党」 ====================== meeting_type_has_dang = False if target_meeting_type_id: meeting_type = self.env['room.meeting.type'].browse(target_meeting_type_id) # 判断会议类型名称是否包含 "党" 字 if meeting_type.name and '党' in meeting_type.name: meeting_type_has_dang = True # 生成议题明细,根据是否含党字设置 is_sanda for issue in issue_active_ids: dept_manager = self.env['hr.employee'].sudo().search([ ('active', '=', True), '|', ('user_role_id.name', '=', '部门经理'), ('user_role_ids.name', '=', '部门经理'), '|', ('department_id', '=', issue.department_id.id), ('department_ids', 'in', [issue.department_id.id]) ], limit=1) # 关键:会议名称含党 → is_sanda=True,否则 False issue_details.append((0, 0, { 'issue_library_id': issue.id, 'employee_id': dept_manager.id if dept_manager else False, 'designated_person_ids': [(6, 0, target_employees.ids)], 'is_sanda': meeting_type_has_dang, # 自动赋值 })) form_id = self.env.ref("room.room_booking_view_form", raise_if_not_found=False) return { "type": "ir.actions.act_window", "name": "批量上会", "res_model": "room.booking", "views": [[form_id.id, "form"]], "context": { 'default_is_issue': True, 'default_state': '申请人', 'default_meeting_type_id': target_meeting_type_id, 'default_start_datetime': fields.Datetime.now() + timedelta(hours=8), 'default_issue_meeting_line_ids': issue_details, }, } def single_meeting_issue(self): issue = self.env["issue.library"].browse(self.id) current_user = self.env.user user_company_name = current_user.company_id.name if current_user.company_id else "" issue_details = [] target_meeting_type_id = False meeting_priority = [ ('is_party', 'is_party_passed', None), # 党总支:动态匹配名称(集团本部=党总支/其他=党支委) ('is_class', 'is_class_passed', '班子'), # 班子会:匹配含「班子」的名称 ('is_boss', 'is_boss_passed', '董事'), # 董事会:匹配含「董事」的名称 ] def get_all_sub_departments(department): dept_ids = [department.id] sub_depts = self.env['hr.department'].search([('parent_id', '=', department.id)]) for sub_dept in sub_depts: dept_ids += get_all_sub_departments(sub_dept) return dept_ids all_dept_ids = get_all_sub_departments(self.department_id) target_employees = self.env['hr.employee'].search([ ('department_id', 'in', all_dept_ids), ('active', '=', True) ]) target_dept_names = ['办公室', '财务部', '风控法务部', '纪检审计部', '综合办公室'] target_managers = self.env['hr.employee'].search([ ('company_id', '=', current_user.company_id.id), ('department_id.name', 'in', target_dept_names), ('active', '=', True), '|', ('user_role_id.name', '=', '部门经理'), ('user_role_ids.name', '=', '部门经理'), ]) manager_employee = self.env['hr.employee'].search([ ('id', 'in', target_managers.ids), '|', ('user_role_id.name', '=', '部门经理'), ('user_role_ids.name', '=', '部门经理') ], limit=1) attendees_ids = [] if target_managers and manager_employee: attendees_ids = target_managers.filtered(lambda emp: emp.id != manager_employee.id).ids elif target_managers: attendees_ids = target_managers.id # 先匹配会议类型 for issue in self: if target_meeting_type_id: continue # 按「党→班子→董事」优先级匹配 for idx, (field_flag, field_passed, keyword) in enumerate(meeting_priority): if not getattr(issue, field_flag, False): continue passed_result = getattr(issue, field_passed, '') if passed_result in (False, 'reject'): # 班子上会:党的 party_char 不能为空 if idx == 1: clean_party = re.sub(r'<[^>]+>', '', issue.party_char or '').strip() if not clean_party: raise UserError("党的意见为空,不能上班子会") # 董事上会:党的 party_char 和班子的 class_char 都不能为空 elif idx == 2: clean_party = re.sub(r'<[^>]+>', '', issue.party_char or '').strip() clean_class = re.sub(r'<[^>]+>', '', issue.class_char or '').strip() if not clean_party: raise UserError("党的意见为空,不能上董事会") if not clean_class: raise UserError("班子意见为空,不能上董事会") if idx == 0: if user_company_name == "集团本部": party_meeting_name = "党总支" else: party_meeting_name = "党支委" meeting_type = self.env['room.meeting.type'].search([ ('name', 'ilike', party_meeting_name) ], limit=1) else: meeting_type = self.env['room.meeting.type'].search([ ('name', 'ilike', keyword) ], limit=1) if meeting_type: target_meeting_type_id = meeting_type.id break elif passed_result == 'pass': continue meeting_type_has_dang = False if target_meeting_type_id: meeting_type = self.env['room.meeting.type'].browse(target_meeting_type_id) if meeting_type.name and '党' in meeting_type.name: meeting_type_has_dang = True for issue in self: issue_details.append((0, 0, { 'issue_library_id': issue.id, 'employee_id': manager_employee.id if manager_employee else False, 'designated_person_ids': [(6, 0, target_employees.ids)], 'attendees_ids': [(6, 0, attendees_ids)], 'is_sanda': meeting_type_has_dang, })) form_id = self.env.ref("room.room_booking_view_form").id return { "type": "ir.actions.act_window", "name": "单个上会", "res_model": "room.booking", "views": [[form_id, "form"]], "context": { 'default_is_issue': True, 'default_state': '申请人', 'default_meeting_type_id': target_meeting_type_id, 'default_start_datetime': fields.Datetime.now() + timedelta(hours=8), 'default_issue_meeting_line_ids': issue_details, } } def approval_details(self): self.ensure_one() for record in self: if not record.issue_approval_id: raise UserError('请提交审批后查看') form_id = self.env.ref("yuthon_issue.issue_library_approval_form").id return{ "type": "ir.actions.act_window", "name": "审批详情", "res_model": "issue.approval", "views": [[form_id, "form"]], "res_id": record.issue_approval_id.id } @api.onchange('issue_library_line_ids') def _onchange_issue_library_line_ids(self): for record in self: record.is_party = bool(record.issue_library_line_ids.filtered(lambda rec: rec.council_id)) record.is_class = bool(record.issue_library_line_ids.filtered(lambda rec: rec.team_meet_id)) record.is_boss = bool(record.issue_library_line_ids.filtered(lambda rec: rec.board_meet_id)) record.is_district_state = bool(record.issue_library_line_ids.filtered(lambda rec: rec.district_state_id)) record.is_district_government = bool( record.issue_library_line_ids.filtered(lambda rec: rec.district_government_id)) record.is_district_party = bool(record.issue_library_line_ids.filtered(lambda rec: rec.district_party_id)) record.is_note = any([ bool(record.issue_library_line_ids.filtered( lambda rec: rec.issue_approval_decision_id and rec.issue_approval_decision_id.district_state_id)), bool(record.issue_library_line_ids.filtered( lambda rec: rec.issue_approval_decision_id and rec.issue_approval_decision_id.district_government_id)), bool(record.issue_library_line_ids.filtered( lambda rec: rec.issue_approval_decision_id and rec.issue_approval_decision_id.district_party_id)) ]) def unlink(self): for i in self: if i.issue_state == 'on_meet': raise UserError('议题已上会,不能删除') return super().unlink() def my_unlink(self): is_admin = self.env.user.has_group('base.group_system') current_user = self.env.user for i in self: if i.issue_state == 'on_meet': raise UserError('议题已上会,不能删除') if not is_admin: if i.employee_id.user_id != current_user: raise UserError('只能删除自己的记录,无权删除他人数据') return super().unlink() @api.depends('name') def _compute_name(self): for record in self: record.name_text = record.name @api.model def name_search(self, name='', args=None, operator='ilike', limit=100): args = args or [] domain = [] if name: domain = ['|', '|', ('name', operator, name), ('code', operator, name), ('name_text', operator, name)] return self.search(domain + args, limit=limit).name_get() @api.onchange('issue_library_room_line_ids') def _onchange_issue_library_room_line_ids(self): for record in self.issue_library_room_line_ids: if record.meeting_type_id.name == '党委会': self.cs_party = record.room_booking_id.name elif record.meeting_type_id.name == '班子会': self.cs_class = record.room_booking_id.name elif record.meeting_type_id.name == '董事会': self.cs_boss = record.room_booking_id.name @api.onchange('is_booking') def _onchange_is_booking(self): if self.is_booking: issue_id = self.env['issue.library'].create({'name': self.name}) self.related_issues_id = issue_id.id workflow_record_id = self.env["soong.workflow.record"].search( [("res_model", "=", self._name), ("res_id", "=", self.id)]) self.env['issue.library.approval.line'].create({ 'issue_library_id': issue_id.id, 'workflow_record_id': workflow_record_id.id, 'approval_type': workflow_record_id.workflow_id.id, 'code': self.code, 'approval_date': fields.Datetime.now(), }) elif self.related_issues_id: raise UserError("已经创建了相关的上会记录,不能取消勾选") class IssueLibraryLine(models.Model): _name = "issue.library.line" _description = '决策清单明细' issue_library_id = fields.Many2one('issue.library') issue_approval_decision_id = fields.Many2one('issue.approval.decision', string="上会依据") council_id = fields.Many2one(related='issue_approval_decision_id.council_id', string='党总支') team_meet_id = fields.Many2one(related='issue_approval_decision_id.team_meet_id', string='经营班子会') board_meet_id = fields.Many2one(related='issue_approval_decision_id.board_meet_id', string='董事会') district_state_id = fields.Many2one(related='issue_approval_decision_id.district_state_id', string="区国资局") district_government_id = fields.Many2one(related='issue_approval_decision_id.district_government_id', string="区政府") district_party_id = fields.Many2one(related='issue_approval_decision_id.district_party_id', string="区委组织部") ordinal = fields.Char(related='issue_approval_decision_id.sequence', string='序号') remarks = fields.Text(related='issue_approval_decision_id.remarks', string="备注") class IssueLibraryApprovalLine(models.Model): _name = "issue.library.approval.line" _description = '关联流程明细' _order = 'approval_date desc' issue_library_id = fields.Many2one('issue.library', string="关联议题") workflow_name = fields.Char(string="标题", compute='compute_issue_soong') code = fields.Char(string="流程编号") approval_type = fields.Char(string="类型", compute='compute_issue_soong') approval_date = fields.Datetime(string="创建日期") res_model = fields.Char(string="模型", compute='compute_issue_soong') res_id = fields.Char(string="id", compute='compute_issue_soong') department_id = fields.Many2one('hr.department', string='部门', compute='compute_issue_soong') @api.depends('code') def compute_issue_soong(self): for rec in self: workflow_record_id = self.env['soong.workflow.record'].search([('code', '=', rec.code)], limit=1) if workflow_record_id: rec.approval_type = workflow_record_id.workflow_id.name rec.res_model = workflow_record_id.res_model rec.res_id = workflow_record_id.res_id rec.workflow_name = workflow_record_id.name record_id = self.env[workflow_record_id.res_model].search([('id', '=', workflow_record_id.res_id)]) if hasattr(record_id, 'user_id') and record_id.user_id: query_user_id = record_id.user_id elif hasattr(record_id, 'users_id') and record_id.users_id: query_user_id = record_id.users_id rec.department_id = query_user_id.employee_ids.department_id.id else: rec.approval_type = '' rec.res_model = '' rec.res_id = '' rec.department_id = '' rec.workflow_name = '' def open_record(self): return { "type": "ir.actions.act_window", "res_model": self.res_model, "res_id": int(self.res_id), "name": self.workflow_name, "view_mode": "form", } class IssueLibraryAttendeeLine(models.Model): _name = "issue.library.attendee.line" _description = '列席人员信息明细' issue_library_id = fields.Many2one('issue.library', string="关联议题") attendee_name = fields.Char(string='姓名') company_id = fields.Many2one('res.company', string='公司') department_id = fields.Many2one('hr.department', string='部门') job_position = fields.Char(string='岗位') class IssueLibraryExternalPersonLine(models.Model): _name = "issue.library.external.person.line" _description = '外部参会人员明细' issue_library_id = fields.Many2one('issue.library', string="关联议题") person_name = fields.Char(string='姓名') company_unit = fields.Char(string='单位/公司') job_title = fields.Char(string='职务') phone = fields.Char(string='联系电话') class IssueLibraryRoomLine(models.Model): _name = "issue.library.room.line" _description = '关联会议明细' issue_library_id = fields.Many2one('issue.library', string="关联议题") room_booking_id = fields.Many2one('room.booking', string="会议名称") room_id = fields.Many2one('room.room', string="会议地点") meeting_type_id = fields.Many2one('room.meeting.type', string="会议类型") approval_date = fields.Datetime(string="创建日期") def open_record(self): self.ensure_one() return { 'type': 'ir.actions.act_window', 'res_model': 'room.booking', 'res_id': self.room_booking_id.id, 'view_mode': 'form', 'target': 'current', } class IssueLibraryAvoidLine(models.Model): _name = 'issue.library.avoid.line' _description = '回避记录' _order = 'avoid_date desc' issue_library_id = fields.Many2one('issue.library', string='关联议题') avoid_user_id = fields.Many2one('res.users', string='回避人', default=lambda self: self.env.user) avoid_date = fields.Datetime(string='回避时间', default=fields.Datetime.now) avoid_comment = fields.Text(string='回避意见')