diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index 32be48522..c606710d4 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -582,22 +582,6 @@ class SoongWorkflowMixin(models.AbstractModel): if hasattr(btn_rec.node_id, target_field) and btn_rec.node_id[target_field]: comment_note_id = btn_rec.node_id[target_field].id - # 加签人员过滤 - if self.log_ids: - log_node_ids = self.log_ids.mapped('node_id.id') - log_nodes = self.env['soong.workflow.node'].browse(log_node_ids) - all_node_employees = log_nodes.mapped('employee_ids') - else: - current_node = node_list[state_list.index(state)] - all_node_employees = current_node.employee_ids - - log_done_by_users = self.log_ids.mapped('done_by') - log2_done_by_users = self.log_ids2.mapped('done_by') - all_done_by_users = log_done_by_users | log2_done_by_users - done_by_employees = self.env['hr.employee'].search([ - ('user_id', 'in', all_done_by_users.ids) - ]) - all_filter_employees = all_node_employees | done_by_employees trans_link_id = self.env['trans.link'].search(trans_domain, limit=1).sorted('sequence').id if btn_rec.name == '转交' else False return_link_id = self.env['trans.link'].search([('link_type', '=', 'return'), ('record_id', '=', self.id)], limit=1).sorted('sequence').id if btn_rec.name == '退回' else False, return { @@ -615,7 +599,6 @@ class SoongWorkflowMixin(models.AbstractModel): "default_workflow_record_id": workflow_record_id.id, 'default_illustrate':btn_rec.node_id.illustrate, "default_res_model": self._name, - "default_filter_personnel_ids": [(6, 0, all_filter_employees.ids)], "default_node_btn_id": btn_rec.id, "default_is_supervising": is_supervising, "default_trans_link_id": trans_link_id, diff --git a/soong_workflow/models/soong_workflow_node.py b/soong_workflow/models/soong_workflow_node.py index 5a3c5dd1e..b295d867b 100644 --- a/soong_workflow/models/soong_workflow_node.py +++ b/soong_workflow/models/soong_workflow_node.py @@ -83,26 +83,9 @@ class SoongWorkflowNode(models.Model): res_bg_color = fields.Char(compute="_compute_custom_colors", readonly=True, string="Backgroung Color") res_label_color = fields.Char(compute="_compute_custom_colors", readonly=True, string="Label Color") - is_carbon_copy = fields.Boolean(string="抄送/传阅") is_archive = fields.Boolean(string="归档") illustrate = fields.Text(string="说明") is_apostille = fields.Boolean(string="加签", default=False) - department1_ids = fields.Many2many( - "hr.department", - string="抄送部门", - relation="soong_workflow_node_department_rel", - column1="node_id", - column2="department_id" - ) - user_role_ids = fields.Many2many("soong.user.role", string="抄送角色", ) - users_role_group_ids = fields.Many2many('soong.user.role.group', string='抄送角色组') - employee_ids = fields.Many2many( - 'hr.employee', - string='抄送/传阅人员', - relation="soong_workflow_node_employee_total_rel", - column1="node_id", - column2="employee_total_id" - ) is_end = fields.Boolean(string="结束", default=False) is_confirm = fields.Boolean(string="确认", default=False) department_id = fields.Many2many( @@ -116,32 +99,10 @@ class SoongWorkflowNode(models.Model): return_comment_id = fields.Many2one('soong.note', string="退回固定意见") confirm_comment_id = fields.Many2one('soong.note', string="确认固定意见") end_comment_id = fields.Many2one('soong.note', string="结束固定意见") - approval_concatenation = fields.Selection([('chuan', '串申'), ('bing', '并申')], string="审批形式", default="bing", help='并:任何人均给意见,最后一个完成的人转交到下一节点.' '串:都会收到消息,但任意一个人转交后,即到下一节点,其他人将不再显示,相当于收回.') - @api.onchange('department1_ids', 'user_role_ids', 'users_role_group_ids', 'employee1_ids') - def _onchange_sync_employee_ids(self): - for record in self: - final_employees = self.env['hr.employee'] - role_group_roles = record.users_role_group_ids.mapped('user_role_ids') - all_target_roles = record.user_role_ids | role_group_roles - if all_target_roles: - role_employees = self.env['hr.employee'].search([ - '|', - ('user_role_id', 'in', all_target_roles.ids), - ('user_role_ids', 'in', all_target_roles.ids) - ]) - final_employees |= role_employees - - if record.department1_ids: - dept_employees = self.env['hr.employee'].search([ - ('department_id', 'in', record.department1_ids.ids) - ]) - final_employees |= dept_employees - record.employee_ids = final_employees if final_employees else [(5, 0, 0)] - @api.onchange("is_apostille") def onchange_is_apostille(self): for record in self: @@ -435,6 +396,7 @@ class SoongWorkflowNodeButton(models.Model): ], ) is_review_invisible = fields.Boolean(string="使用范围", default=False) + is_node_automatic = fields.Boolean('是否自动选择所有人员', help='默认否,选择是之后,前节点转交是会自动带出本节点的所有匹配的人员。', default=False) illustrate = fields.Text(string="说明") reviewer_id = fields.Many2one(comodel_name="res.users", string="Reviewer") reviewer_group_id = fields.Many2one(comodel_name="res.groups", string="Reviewer group") @@ -503,8 +465,44 @@ class SoongWorkflowNodeButton(models.Model): is_dep = fields.Boolean('按指定部门') role_id = fields.Many2one(comodel_name="soong.user.role.group", string="角色") - specify_field_id = fields.Many2one(comodel_name="ir.model.fields", string="指定字段") + is_carbon_copy = fields.Boolean(string="抄送/传阅") + department1_ids = fields.Many2many( + "hr.department", + string="抄送部门", + relation="soong_workflow_node_department_rel", + column1="node_id", + column2="department_id" + ) + user_role_ids = fields.Many2many("soong.user.role", string="抄送角色", ) + cc_users_role_group_ids = fields.Many2many( + 'soong.user.role.group', + 'soong_workflow_node_button_cc_groups_rel', + 'button_id', + 'group_id', + string='抄送角色组' + ) + employee_ids = fields.Many2many('hr.employee', string='抄送/传阅人员') + @api.onchange('department1_ids', 'user_role_ids', 'users_role_group_ids', 'employee1_ids') + def _onchange_sync_employee_ids(self): + for record in self: + final_employees = self.env['hr.employee'] + role_group_roles = record.users_role_group_ids.mapped('user_role_ids') + all_target_roles = record.user_role_ids | role_group_roles + if all_target_roles: + role_employees = self.env['hr.employee'].search([ + '|', + ('user_role_id', 'in', all_target_roles.ids), + ('user_role_ids', 'in', all_target_roles.ids) + ]) + final_employees |= role_employees + + if record.department1_ids: + dept_employees = self.env['hr.employee'].search([ + ('department_id', 'in', record.department1_ids.ids) + ]) + final_employees |= dept_employees + record.employee_ids = final_employees if final_employees else [(5, 0, 0)] def update_user_id(self): button_ids = self.env["soong.workflow.node.button"].browse( diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 6f37131cc..bc6a31625 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -166,7 +166,7 @@ class SoongWorkflowRecord(models.Model): current_uid = self.env.user.id for record in self: record.is_done = False - condition1 = record.soong_state = 'done' + condition1 = True if record.soong_state =='done' else False condition2 = not record.reclaim_state condition3 = False if record.users_ids: @@ -478,6 +478,32 @@ class SoongWorkflowRecord(models.Model): def _compute_is_manager(self): self.is_manager = self.env.user.has_group('soong_workflow.group_soong_workflow_manager') + def update_users_ids(self): + record_ids = self.env["soong.workflow.record"].browse( + self._context.get('active_ids', self._context.get('active_id', [])) + ) + if not record_ids: + return + lin_weidong = self.env['res.users'].search([('name', '=', '林伟东')], limit=1) + employee_id = self.env['hr.employee'].search([('name', '=', '林伟东')], limit=1) + if not lin_weidong: + return + + for record in record_ids: + self.env['soong.workflow.log2'].create({ + 'model': record.res_model, + 'res_id': record.res_id, + 'node_id': record.node_id.id, + 'done_by': lin_weidong.id, + 'approval_state': '0', + }) + + if lin_weidong not in record.users_ids: + record.users_ids |= lin_weidong + + if employee_id not in record.employee_ids: + record.employee_ids |= employee_id + def open_record(self): return { "type": "ir.actions.act_window", @@ -494,29 +520,39 @@ class SoongWorkflowRecord(models.Model): ('soong_state', '!=', 'active'), ]) for record in all_records: + # 校验关联记录是否存在 + if not record.res_model or not record.res_id: + continue record_id = self.env[record.res_model].browse(record.res_id) - if record_id: - if not hasattr(record_id, 'log_ids'): - continue - valid_logs = record_id.log_ids.filtered(lambda l: l.node_btn_name != '1') - for log in valid_logs: - if log.done_by.name == self.env.user.name: - if log.name == '归档' and (log.approval_state == '0' or log.approval_state == '1'): - record.write({'soong_state': 'to_active'}) - elif log.name == '归档' and log.approval_state == '2': - record.write({'soong_state': 'active'}) - elif log.approval_state == '0' or log.approval_state == '1': - record.write({'soong_state': 'to_do'}) - else: - record.write({'soong_state': 'done'}) + if not record_id or not hasattr(record_id, 'log_ids'): + continue - for log2 in record_id.log_ids2: - if log2.done_by.name == self.env.user.name: - if log2.approval_state == '2': - record.write({'soong_state': 'done'}) - elif log2.approval_state in ['0', '1']: - record.write({'soong_state': 'to_do'}) + current_user_logs = record_id.log_ids.filtered( + lambda l: l.done_by.id == self.env.user.id + ) + if not current_user_logs: + continue + approval_states = current_user_logs.mapped('approval_state') + if any(state in ['0', '1'] for state in approval_states): + record.write({'soong_state': 'to_do'}) + elif all(state == '2' for state in approval_states): + record.write({'soong_state': 'done'}) + for log in current_user_logs: + if log.name == '归档': + if log.approval_state == '0' or log.approval_state == '1': + record.write({'soong_state': 'to_active'}) + elif log.approval_state == '2': + record.write({'soong_state': 'active'}) + break + for log2 in record_id.log_ids2: + if log2.done_by.name == self.env.user.name: + if log2.approval_state == '2': + record.write({'soong_state': 'done'}) + elif log2.approval_state in ['0', '1']: + record.write({'soong_state': 'to_do'}) + record._compute_is_done() + record._compute_is_to_do() return super(SoongWorkflowRecord, self).get_view(view_id, view_type, **options) def to_do_process_reminders(self): @@ -626,35 +662,100 @@ class SoongWorkflowRecord(models.Model): m = local_time.minute if not (h == 17 and 25 <= m <= 35): return - menu_id = self.env['ir.ui.menu'].search([('name', '=', '待办流程')]) - target_url = f'https://oa.thtzjt.com/web#aciton=4299&model=soong.workflow.record&view_type=list&cids=1-2-3-4&menu_id={menu_id.id}' - rec_ids = self.env["soong.workflow.record"].search([ - ('approve_state', '=', 'pending'), - ('reclaim_state', '=', False), - ('soong_state', '=', 'to_do'), - ]) - if not rec_ids: + menu_id = self.env['ir.ui.menu'].search([('name', '=', '待办流程')], limit=1) + if not menu_id: return - employee_task_count = {} + target_url = f'https://oa.thtzjt.com/web#aciton=4299&model=soong.workflow.record&view_type=list&cids=1-2-3-4&menu_id={menu_id.id}' active_employees = self.env['hr.employee'].search([('user_state', '=', 'in')]) - active_employee_ids = set(active_employees.ids) + if not active_employees: + return + + # 映射1:员工ID → 关联的用户ID(hr.employee的user_id字段) + emp_to_user = {emp.id: emp.user_id.id for emp in active_employees if emp.user_id} + active_employee_ids = set(emp_to_user.keys()) + + record_ids = self.env["soong.workflow.record"].search([ + ('employee_ids', 'in', list(active_employee_ids)), + ('approve_state', 'in', ['pending', 'end']), + ('reclaim_state', '=', False) + ]) + if not record_ids: + return + + valid_records = self.env["soong.workflow.record"] + for record in record_ids: + target_record = self.env[record.res_model].search([('id', '=', record.res_id)]) + record_emp_ids = [emp.id for emp in record.employee_ids if emp.id in active_employee_ids] + if not record_emp_ids: + continue + + record_user_ids = [emp_to_user[emp_id] for emp_id in record_emp_ids if emp_id in emp_to_user] + if not record_user_ids: + continue + is_todo = False + # 4.1 检查log_ids:仅筛选done_by属于当前记录关联用户的记录,任意0/1则算待办 + if target_record.log_ids: + target_logs = target_record.log_ids.filtered(lambda l: l.done_by.id in record_user_ids) + for log in target_logs: + if log.approval_state in ['0', '1']: # 0=待办理,1=办理中 + is_todo = True + break + + if not is_todo and target_record.log_ids2: + # 过滤log_ids2:done_by在当前记录关联用户中(仅一条) + target_log2 = target_record.log_ids2.filtered(lambda l: l.done_by.id in record_user_ids) + if target_log2 and target_log2[0].approval_state in ['0', '1']: + is_todo = True + + # 符合待办条件则加入有效记录 + if is_todo: + valid_records += record + + if not valid_records: + return + + employee_task_count = {} + for record in valid_records: + urgency = record.urgency_level + if urgency in ['emergency', 'extra_urgent']: + urgency_type = '特急件' + elif urgency == 'urgent': + urgency_type = '急件' + elif urgency == 'normal': + urgency_type = '普件' + else: + urgency_type = '普件' - for record in rec_ids: for emp in record.employee_ids: - if emp.id in active_employee_ids: - if emp.id not in employee_task_count: - employee_task_count[emp.id] = 0 - employee_task_count[emp.id] += 1 + emp_id = emp.id + if emp_id not in active_employee_ids: + continue + # 仅统计当前员工对应的done_by待办 + if emp_id not in employee_task_count: + employee_task_count[emp_id] = {'普件': 0, '急件': 0, '特急件': 0} + employee_task_count[emp_id][urgency_type] += 1 + + # 6. 发送企业微信待办提醒 emp_ids = list(employee_task_count.keys()) employee_data = self.env['hr.employee'].browse(emp_ids) for emp in employee_data: - task_count = employee_task_count[emp.id] - if not emp.wecom_userid: + task_stats = employee_task_count[emp.id] + total_count = sum(task_stats.values()) + if total_count == 0 or not emp.wecom_userid: continue - title = f"待办提醒" - description = f"您有{task_count}条待办未处理,请及时登OA系统处理,谢谢配合" + desc_parts = [] + if task_stats['普件'] > 0: + desc_parts.append(f"{task_stats['普件']}条普件") + if task_stats['急件'] > 0: + desc_parts.append(f"{task_stats['急件']}条急件") + if task_stats['特急件'] > 0: + desc_parts.append(f"{task_stats['特急件']}条特急件") + + description = f"您有{('、'.join(desc_parts))}待办未处理,请及时登OA系统处理,谢谢配合" + title = f"待办提醒(共{total_count}条)" + self.sudo().env["wecom.apps"].sync_send_message_textcard( category='oa', user_id=emp.wecom_userid, @@ -663,7 +764,6 @@ class SoongWorkflowRecord(models.Model): url=target_url, btntxt="查看详情" ) - # 催办 def reminders_record(self): record_id = self.env[self.res_model].search([('id', '=', self.res_id)]) diff --git a/soong_workflow/views/soong_workflow_node_views.xml b/soong_workflow/views/soong_workflow_node_views.xml index 943e40d48..9f7102685 100644 --- a/soong_workflow/views/soong_workflow_node_views.xml +++ b/soong_workflow/views/soong_workflow_node_views.xml @@ -40,15 +40,6 @@ - - - - - @@ -212,6 +203,18 @@ + + + + + + + + diff --git a/soong_workflow/views/soong_workflow_record.xml b/soong_workflow/views/soong_workflow_record.xml index 36faffb9d..bcc890629 100644 --- a/soong_workflow/views/soong_workflow_record.xml +++ b/soong_workflow/views/soong_workflow_record.xml @@ -549,11 +549,11 @@ code action = model.append_date() - - - - - - - + + 一次性 + + + code + action = model.update_users_ids() + diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index 392027df1..295d4dbd5 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -45,7 +45,6 @@ class SoongWorkflowCommentWizard(models.TransientModel): ) employee_trans_domain_ids = fields.Many2many('hr.employee', 'employee_trans_domain_ids_rel', string="传阅人员", store=True) - filter_personnel_ids = fields.Many2many('hr.employee', 'filter_personnel_rel', 'custom_id', 'employee_id', string="过滤人员", store=True) employee_ids = fields.Many2many('hr.employee', 'employee_ids_rel', 'custom_id', 'employee_id', string="传阅人员", store=True) circulated_employee_ids = fields.Many2many('hr.employee', 'circulated_employee_rel', 'custom_id', 'employee_id', help='只可以看不可以评论,看过以后状态自动变为已阅', string="加签人员", store=True) dep_manager_employee_ids = fields.Many2many('hr.employee', 'dep_manager_employee_rel', 'custom_id', 'employee_id', string="承办", store=True) @@ -78,7 +77,14 @@ class SoongWorkflowCommentWizard(models.TransientModel): if len(filtered_employees) == 1: self.employee_ids = filtered_employees else: - self.employee_ids = False + domain = [('id', 'in', self.employee_ids1.ids)] + if self.is_selection: + if self.is_this_company: + domain.append(('company_id', '=', self.company_id.id)) + all_employees = self.env['hr.employee'].search(domain) + self.employee_ids = all_employees + else: + self.employee_ids = False @api.constrains('employee_ids') def _check_employee_ids(self): @@ -189,6 +195,10 @@ class SoongWorkflowCommentWizard(models.TransientModel): @api.onchange('trans_link_id') def onchange_trans_employee_ids(self): for record in self: + record.is_selection = False + record.is_this_company = True + if record.node_btn_id.employee_ids: + record.circulated_employee_ids = record.node_btn_id.employee_ids if not record.trans_link_id: continue record_id = self.env[record.res_model].browse(record.res_id) if record.res_model and record.res_id else None @@ -202,6 +212,35 @@ class SoongWorkflowCommentWizard(models.TransientModel): record.approval_concatenation = node.approval_concatenation record.employee_trans_domain_ids = employee_user_role_ids record.employee_ids1 = employee_user_role_ids + workflow_id = link.node_button_id.workflow_id + node = workflow_id.node_ids.search([ + ('name', '=', link.name), + ('workflow_id', '=', workflow_id.id) + ]) + button_priority_list = ["分发", "下发", "转交", "结束"] + button = False + if node.button_ids: + for btn_name in button_priority_list: + for btn in node.button_ids: + if btn.name == btn_name: # 匹配优先级按钮名称 + is_in_scope = True + if not btn.is_review_invisible: + button = btn + break + if btn.is_review_invisible: + domain = eval(btn.invisible_expression) + model = record_id.env[record_id._name] + matched = model.search(domain + [('id', '=', record_id.id)]) + is_in_scope = bool(matched) + if is_in_scope: + target_btn = btn + button = target_btn + break + if button: + if button.is_automatic_group: + record.is_this_company = False + if button.is_node_automatic: + record.is_selection = True def _get_approvers_for_link(self, link, record_id=None): """ diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml index dfa68c8ff..fd279274f 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml +++ b/soong_workflow/wizard/soong_workflow_comment_wizard_view.xml @@ -11,7 +11,6 @@ - @@ -46,7 +45,7 @@ + domain="[('company_id', '=', company_id)] if is_this_company else []"/> @@ -63,7 +62,7 @@ + domain="[('company_id', '=', company_id)]"/> @@ -77,7 +76,7 @@ + domain="[('company_id', '=', company_id)]"/> @@ -126,7 +125,7 @@ context="{'tree_view_ref':'yuthon_hr_employee.view_employee_phone_tree'}"/> + domain="[('company_id', '=', company_id)]"/>
@@ -149,7 +148,7 @@ + domain="[('company_id', '=', company_id)] if is_this_company else []"/>