From 8802c11e9db3fedfd8d086f1cc4443381817d256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Sat, 6 Dec 2025 18:53:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- soong_workflow/models/soong_workflow_mixin.py | 1 + soong_workflow/models/soong_workflow_node.py | 237 ++++++++++++++++++ yuthon_hr_attendance/models/hr_attendance.py | 9 +- yuthon_wechat/models/wecom_apps.py | 9 +- 4 files changed, 247 insertions(+), 9 deletions(-) diff --git a/soong_workflow/models/soong_workflow_mixin.py b/soong_workflow/models/soong_workflow_mixin.py index e7f384f3f..e48d74c55 100644 --- a/soong_workflow/models/soong_workflow_mixin.py +++ b/soong_workflow/models/soong_workflow_mixin.py @@ -267,6 +267,7 @@ class SoongWorkflowMixin(models.AbstractModel): } } else: + btn_rec.with_context(cx).log() return btn_rec.with_context(cx).run() def _add_comment(self, logs, btn_rec): diff --git a/soong_workflow/models/soong_workflow_node.py b/soong_workflow/models/soong_workflow_node.py index 787412515..263f00f1f 100644 --- a/soong_workflow/models/soong_workflow_node.py +++ b/soong_workflow/models/soong_workflow_node.py @@ -555,6 +555,242 @@ class SoongWorkflowNodeButton(models.Model): if res: rec.btn_key = self._generate_key() + def log(self): + cx = self.env.context.copy() or {} + query_user_id = self.env.user.id + is_admin = self.env.user.has_group('soong_workflow.group_soong_workflow_manager') + res_model = cx.get("active_model", False) + res_id = cx.get("active_id", 0) + record_id = self.env[res_model].search([('id', '=', res_id)]) + log = self.env['soong.workflow.log'].search([("model", "=", res_model), + ("res_id", "=", res_id)]) + employee_user_role_ids = self._get_approvers_for_link(record_id) + if is_admin and record_id: + if hasattr(record_id, 'user_id') and record_id.user_id: + query_user_id = record_id.user_id.id + elif hasattr(record_id, 'users_id') and record_id.users_id: + query_user_id = record_id.users_id.id + + if len(log) > 1 and is_admin: + current_node_log = self.sudo().env["soong.workflow.log"].search( + [("model", "=", res_model), + ("res_id", "=", res_id), + ("node_id", "=", self.node_id.id)]) + else: + current_node_log = self.sudo().env["soong.workflow.log"].search( + [("model", "=", res_model), + ("res_id", "=", res_id), + ("done_by", "=", query_user_id), + ("node_id", "=", self.node_id.id)]) + # 创建或更新当前用户的审批记录 + if not current_node_log: + self.sudo().env["soong.workflow.log"].create({ + "model": res_model, + "res_id": res_id, + "node_id": self.node_id.id, + "workflow_id": self.workflow_id.id, + "done_by": query_user_id, + "node_btn_name": "0", + "node_btn_id": self.id, + "lase_click_date": fields.Datetime.now(), + "approval_state": "2", # 已完成 + }) + else: + current_node_log.write({ + "approval_state": "2", # 已完成 + "lase_click_date": fields.Datetime.now() + }) + + expected_employees = employee_user_role_ids + + # 查找下一个节点 + next_node_id = record_id.workflow_id.node_ids.search([ + ('name', '=', self.link_id.node_to.name), + ('workflow_id', '=', self.workflow_id.id) + ]) + + completed_logs = self.sudo().env["soong.workflow.log"].search([ + ("model", "=", res_model), + ("res_id", "=", res_id), + ("node_id", "=", self.node_id.id), + ("node_btn_name", "=", "4"), + ("approval_state", "!=", "2") + ]) + all_completed = len(completed_logs) == 0 + if all_completed: + for employee in expected_employees: + # 检查该员工在下一节点是否已有记录 + next_node_employee_log = self.sudo().env["soong.workflow.log"].search([ + ("model", "=", res_model), + ("res_id", "=",res_id), + ("node_id", "=", next_node_id.id), + ('node_btn_name', '!=', '1'), + ("done_by", "=", employee.user_id.id) + ]) + + if not next_node_employee_log: + # 创建初步记录,状态为"待审批" + self.sudo().env["soong.workflow.log"].create({ + "model": res_model, + "res_id":res_id, + "node_id": next_node_id.id, + "workflow_id": self.workflow_id.id, + "done_by": employee.user_id.id, + "node_btn_name": "4", + "approval_state": "0", # 待审批 + }) + workflow_record_id = self.env['soong.workflow.record'].search([('code', '=', record_id.code)]) + if employee_user_role_ids: + workflow_record_id.users_ids = [(4, employee.user_id.id) for employee in employee_user_role_ids if + employee.user_id] + workflow_record_id.employee_ids = employee_user_role_ids + workflow_record_id.approve_state = "pending" # 待审批 + related_record = self.env[res_model].browse(res_id) + if hasattr(related_record, 'self_users_ids'): + user_ids = [employee.user_id.id for employee in employee_user_role_ids if employee.user_id] + related_record.self_users_ids = [(6, 0, user_ids)] if user_ids else [(5,)] + record_id.write({'state': self.link_id.node_to.name}) + + for employee in employee_user_role_ids: + action_id = self.env['ir.actions.act_window'].search([('res_model', '=', res_model)])[0] + url = f'https://oa.thtzjt.com/web?login={employee.user_id.login}&oauth_uid={employee.wecom_userid}#id={record_id.id}&cids=2&action={action_id.id}&model={record_id._name}&view_type=form&menu_id=10104' + self.sudo().env["wecom.apps"].sync_send_message_textcard(category='oa', + user_id=employee.wecom_userid, + title=record_id._description, + description="标题: {}\n".format( + record_id.name) + self.env.user.name + ' ' + str( + fields.Datetime.now() + timedelta( + hours=8)), + url=url, btntxt="查看详情") + + def _get_approvers_for_link(self, record_id=None): + node = self.workflow_id.node_ids.search([ + ('name', '=', self.link_id.node_to.name), + ('workflow_id', '=', self.workflow_id.id) + ]) + button = False + if node.button_ids: + # 1. 优先找“分发”按钮 + distribute_btn = next((btn for btn in node.button_ids if btn.name == '分发'), None) + if distribute_btn: + button = distribute_btn + else: + # 2. 找不到“分发”再找“转交” + transfer_btn = next((btn for btn in node.button_ids if btn.name == '转交'), None) + if transfer_btn: + button = transfer_btn + else: + # 3. 最后找“结束” + button = next((btn for btn in node.button_ids if btn.name == '结束'), None) + + if not button: + return self.env['hr.employee'] + + link_users_id = button.users_role_id + link_users_group_ids = button.users_role_group_ids + is_automatic = button.is_automatic + is_automatic_company = button.is_automatic_company + is_automatic_group = button.is_automatic_group + is_logged_by = button.is_logged_by + is_logged_company = button.is_logged_company + is_logged_group = button.is_logged_group + is_dep = button.is_dep + is_field = button.specify_field_id + + if button.reviewer_id: + employee = self.env['hr.employee'].search([ + ('user_id', '=', button.reviewer_id.id), + ], limit=1) + return employee + + # 部门和公司筛选条件 + department_domain = [] + company_domain = [] + user_role_domain = [] + + if record_id: + # 获取当前用户和申请人的相关信息 + current_user = self.env.user + applicant = None + + # 获取申请人的用户对象 + for field_name in ['user_id', 'users_id']: + if hasattr(record_id, field_name): + applicant = getattr(record_id, field_name) + if applicant: + break + + # 根据勾选的条件设置域条件(只勾选一个) + if is_automatic and record_id.department_id: + # 按当前申请人自动匹配-本部门(保留部门和公司条件) + department_domain = ['|', ('department_id', '=', record_id.department_id.id), + ('department_ids', 'in', record_id.department_id.id)] + company_domain = [('company_id', '=', applicant.company_id.id)] + + elif is_automatic_company and applicant and hasattr(applicant, 'company_id'): + # 按当前申请人自动匹配-本公司(仅保留公司条件) + company_id = applicant.company_id.id + company_domain = [('company_id', '=', company_id)] + + elif is_automatic_group and applicant and hasattr(applicant, 'company_id'): + # 按当前申请人自动匹配-本集团(仅保留公司条件) + parent_company = applicant.company_id.parent_id + if parent_company: + group_company_ids = self.env['res.company'].search([('id', 'child_of', parent_company.id)]).ids + company_domain = [('company_id', 'in', group_company_ids)] + + elif is_dep and hasattr(record_id, 'department_id1') and record_id.department_id1: + department_id1 = record_id.department_id1.id + department_domain = ['|', ('department_id', '=', department_id1), + ('department_ids', 'in', department_id1)] + user_role_domain = ['|', ('user_role_id', '=', button.role_id.user_role_ids.ids), + ('user_role_ids', 'in', button.role_id.user_role_ids.ids)] + + elif is_logged_by and current_user.department_id: + # 按当前登录人自动匹配-本部门(保留部门和公司条件) + department_domain = ['|', ('department_id', '=', current_user.department_id.id), + ('department_ids', 'in', current_user.department_id.id)] + company_domain = [('company_id', '=', current_user.company_id.id)] + + elif is_logged_company: + # 按当前登录人自动匹配-本公司(仅保留公司条件) + company_id = current_user.company_id.id + company_domain = [('company_id', '=', company_id)] + + elif is_logged_group: + # 按当前登录人自动匹配-本集团(仅保留公司条件) + parent_company = current_user.company_id.parent_id + if parent_company: + group_company_ids = self.env['res.company'].search([('id', 'child_of', parent_company.id)]).ids + company_domain = [('company_id', 'in', group_company_ids)] + elif is_field: + field_name = button.specify_field_id.name + # 目前只会有入职 + field_value = record_id[field_name] + department_domain = ['|', ('department_id', '=', field_value.id), + ('department_ids', 'in', field_value.id)] + + if link_users_id: + role_domain = ['|', ('user_role_id', '=', link_users_id.id), ('user_role_ids', 'in', link_users_id.id)] + if user_role_domain: + role_domain = ['|'] + role_domain + user_role_domain + domain = role_domain + company_domain + department_domain + return self.env['hr.employee'].sudo().search(domain) + + elif link_users_group_ids: + role_domain = ['|', ('user_role_id', 'in', link_users_group_ids.user_role_ids.ids), + ('user_role_ids', 'in', link_users_group_ids.user_role_ids.ids)] + if user_role_domain: + role_domain = ['|'] + role_domain + user_role_domain + domain = role_domain + company_domain + department_domain + return self.env['hr.employee'].sudo().search(domain) + + # 特殊分发按钮 + if '分发' in link.name and record_id and hasattr(record_id, 'users_id'): + return record_id.users_id.employee_id + return self.env['hr.employee'] + + def run(self): for rec in self: # Check Condition Before Executing Action @@ -627,6 +863,7 @@ class SoongWorkflowNodeButton(models.Model): # # 发送信息提醒 # partner_ids = self.sudo().env['res.users'].search([('id', 'in', user_ids)]).mapped('partner_id').ids # self.sudo().with_context(lang=self.env.lang)._message_auto_subscribe_notify(partner_ids, 'soong_workflow.message_yuthon_workflow_btn_approval_data') + except Exception as e: pass return func_result diff --git a/yuthon_hr_attendance/models/hr_attendance.py b/yuthon_hr_attendance/models/hr_attendance.py index 89bd543f3..2c9f0c017 100644 --- a/yuthon_hr_attendance/models/hr_attendance.py +++ b/yuthon_hr_attendance/models/hr_attendance.py @@ -964,14 +964,13 @@ class HrAttendance(models.Model): method_name='executive_day_remind_out', is_executive=True) def card_in_comment(self, employee_id, planning_name): - date = (f"
您的班次是{planning_name}还有5分钟
" - f"
上班,记得打卡
") + data = "您的班次是{}\n还有5分钟上班,记得打卡".format(planning_name) url = '#小程序://天投云/XttsPpKg3MEpY7h' try: self.sudo().env["wecom.apps"].sync_send_message_textcard( category='att', - user_id='18562027762', - description=date, + user_id=employee_id.wecom_userid, + description=data, title="打卡提醒", url=url, btntxt="详细信息" @@ -980,7 +979,7 @@ class HrAttendance(models.Model): _logger.error(f"上班打卡提醒发送失败:员工={employee_id.name}, 错误={e}") def card_out_comment(self, employee_id, planning_name): - data = f"您的班次是{planning_name}您该下班了,记得打卡" + data = f"您的班次是{planning_name}\n您该下班了,记得打卡" url = '#小程序://天投云/XttsPpKg3MEpY7h' try: self.sudo().env["wecom.apps"].sync_send_message_textcard( diff --git a/yuthon_wechat/models/wecom_apps.py b/yuthon_wechat/models/wecom_apps.py index a3d4d5c8a..030e56573 100644 --- a/yuthon_wechat/models/wecom_apps.py +++ b/yuthon_wechat/models/wecom_apps.py @@ -150,12 +150,13 @@ class WeComApps(models.Model): elif category == 'att': data = { "touser": user_id, - "msgtype": "miniprogram_notice", - "miniprogram_notice": { - "appid": "wxda530cb253dec90d", - "page": "pages/clock_in/clock_in", + "msgtype": "textcard", + 'agentid': wecom_app.agent_id, + "textcard": { "title": "打卡提醒", "description": description, + "url": url, + "btntxt": btntxt, }, } else: