From 37446da00f4b932b03befe87281ce391b72d1fef 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: Fri, 20 Mar 2026 17:02:24 +0800 Subject: [PATCH 01/19] =?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 --- .../models/soong_workflow_record.py | 279 +++++------------- 1 file changed, 80 insertions(+), 199 deletions(-) diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 713a7428f..6ca409b45 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -169,155 +169,113 @@ class SoongWorkflowRecord(models.Model): _logger.warning(f"Failed to get urgency_level from {record.res_model}: {e}") record.urgency_level = 'normal' - @api.depends('employee_ids', 'res_model', 'res_id', 'soong_state', 'reclaim_state', 'approve_state') + @api.depends('employee_ids', 'res_model', 'res_id', 'soong_state', 'reclaim_state', 'approve_state', 'users_ids') def _compute_is_to_do(self): - """保留原有计算逻辑(已优化批量查询,参考之前的版本)""" current_uid = self.env.user.id - current_employees = self.env['hr.employee'].search([('user_id', '=', current_uid)]) - active_employee_ids = current_employees.ids if current_employees else [] - if not active_employee_ids: - self.is_to_do = False - return - # 批量优化:预加载员工映射(仅查1次) - all_employees = self.env['hr.employee'].search([('user_state', '=', 'in')]) - emp_to_user = {emp.id: emp.user_id.id for emp in all_employees if emp.user_id} - - # 第一步:批量处理条件1(approve_state=start) - condition1_records = self.filtered( - lambda r: not r.reclaim_state and r.approve_state == 'start' and - set(active_employee_ids) & set(r.employee_ids.ids + r.employee2_ids.ids) + for rec in self: + rec.is_to_do = False + valid_records = self.filtered( + lambda r: current_uid in r.users_ids.ids ) - condition1_records.is_to_do = True - - # 第二步:批量处理条件2(log_ids/log_ids2) - remaining_records = self - condition1_records - remaining_records.is_to_do = False - - # 按res_model分组批量查询log(核心优化) - model_res_ids = defaultdict(list) - rec_model_res_map = {} - for rec in remaining_records.filtered(lambda r: not r.reclaim_state and r.res_model and r.res_id): - model_res_ids[rec.res_model].append(rec.res_id) - rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) - - # 批量查log满足条件的记录ID - valid_rec_ids = [] - for model_name, res_ids in model_res_ids.items(): - if not res_ids: - continue - TargetModel = self.env[model_name].sudo() + if not valid_records: + return + start_records = valid_records.filtered(lambda r: r.approve_state == 'start') + start_records.is_to_do = True + remaining_records = valid_records - start_records + if not remaining_records: + return + for rec in remaining_records: log_domain = [ - ('res_id', 'in', res_ids), - ('done_by', 'in', list(emp_to_user.values())), + ("model", "=", rec.res_model ), + ("res_id", "=", rec.res_id), + ('done_by', '=', current_uid), ('approval_state', 'in', ['0', '1']) ] - # 批量查log_ids - log1_valid_res_ids = TargetModel.log_ids.search(log_domain).mapped('res_id') - # 批量查log_ids2 - log2_valid_res_ids = TargetModel.log_ids2.search(log_domain).mapped('res_id') - - valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) - for rec_id, (model, res_id) in rec_model_res_map.items(): - if model == model_name and res_id in valid_res_ids: - valid_rec_ids.append(rec_id) - self.browse(valid_rec_ids).is_to_do = True + has_valid_log1 = self.env['soong.workflow.log'].search_count(log_domain) > 0 + has_valid_log2 = self.env['soong.workflow.log2'].search_count(log_domain) > 0 + if has_valid_log1 or has_valid_log2: + rec.is_to_do = True def _search_is_to_do(self, operator, value): - """搜索待办状态:仅打印总耗时""" - current_uid = self.env.user.id - current_employees = self.env['hr.employee'].sudo().search([('user_id', '=', current_uid)]) - active_employee_ids = current_employees.ids if current_employees else [] - if not active_employee_ids: + # 第一步:基础过滤 - 只处理包含当前用户的记录 + base_domain = [('users_ids', 'in', [current_uid])] + candidate_records = self.search(base_domain) + + if not candidate_records: return [('id', '=', False)] - base_domain = [ - ('reclaim_state', '=', False), - '|', - ('employee_ids', 'in', active_employee_ids), - ('employee2_ids', 'in', active_employee_ids) - ] - + # 处理 "is_to_do = True" 或 "is_to_do != False" 的情况 if (operator == '=' and value) or (operator == '!=' and not value): - candidate_records = self.search(base_domain) - if not candidate_records: - return [('id', '=', False)] - + # 条件1:approve_state为start的记录ID condition1_ids = candidate_records.filtered( - lambda r: r.approve_state == 'start' and not r.reclaim_state + lambda r: r.approve_state == 'start' ).ids - model_res_ids = defaultdict(list) - rec_model_res_map = {} - for rec in candidate_records - self.browse(condition1_ids): - if rec.res_model and rec.res_id: - model_res_ids[rec.res_model].append(rec.res_id) - rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) + # 条件2:非start状态,且日志符合条件的记录ID + remaining_records = candidate_records - self.browse(condition1_ids) + # 仅筛选有res_model/res_id且非草稿/非start的记录 + target_records = remaining_records.filtered( + lambda r: r.res_model and r.res_id and r.approve_state not in ['draft', 'start'] + ) condition2_rec_ids = [] - for model_name, res_ids in model_res_ids.items(): - if not res_ids: - continue - TargetModel = self.env[model_name].sudo() - log1_domain = [ - ('res_id', 'in', res_ids), - ('done_by', '=', current_uid), - ('approval_state', 'in', ['0', '1']) - ] - log1_valid_res_ids = TargetModel.log_ids.search(log1_domain).mapped('res_id') - log2_domain = [ - ('res_id', 'in', res_ids), - ('done_by', '=', current_uid), - ('approval_state', 'in', ['0', '1']) - ] - log2_valid_res_ids = TargetModel.log_ids2.search(log2_domain).mapped('res_id') + if target_records: + # 逐条检查日志(完全对齐compute逻辑,移除try-except) + for rec in target_records: + # 日志筛选条件(和compute逻辑完全一致) + log_domain = [ + ("model", "=", rec.res_model), + ("res_id", "=", rec.res_id), + ('done_by', '=', current_uid), + ('approval_state', 'in', ['0', '1']) + ] + # 直接查询soong.workflow.log和log2模型 + has_valid_log1 = self.env['soong.workflow.log'].search_count(log_domain) > 0 + has_valid_log2 = self.env['soong.workflow.log2'].search_count(log_domain) > 0 - valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) - for rec_id, (model, res_id) in rec_model_res_map.items(): - if model == model_name and res_id in valid_res_ids: - condition2_rec_ids.append(rec_id) + if has_valid_log1 or has_valid_log2: + condition2_rec_ids.append(rec.id) + # 合并符合条件的记录ID valid_ids = list(set(condition1_ids + condition2_rec_ids)) - domain = [('id', 'in', valid_ids)] + domain = [('id', 'in', valid_ids)] if valid_ids else [('id', '=', False)] elif (operator == '=' and not value) or (operator == '!=' and value): - candidate_records = self.search(base_domain) - if not candidate_records: - return [('id', '=', False)] - - condition1_invalid_ids = candidate_records.filtered( - lambda r: r.approve_state == 'start' and not r.reclaim_state + todo_ids = [] + # 条件1:approve_state为start的待办ID + condition1_todo_ids = candidate_records.filtered( + lambda r: r.approve_state == 'start' ).ids - model_res_ids = defaultdict(list) - rec_model_res_map = {} - for rec in candidate_records - self.browse(condition1_invalid_ids): - if rec.res_model and rec.res_id: - model_res_ids[rec.res_model].append(rec.res_id) - rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) + # 条件2:日志符合条件的待办ID + remaining_records = candidate_records - self.browse(condition1_todo_ids) + target_records = remaining_records.filtered( + lambda r: r.res_model and r.res_id and r.approve_state not in ['draft', 'start'] + ) - condition2_invalid_ids = [] - for model_name, res_ids in model_res_ids.items(): - if not res_ids: - continue - TargetModel = self.env[model_name].sudo() - log1_domain = [ - ('res_id', 'in', res_ids), - ('done_by', '=', current_uid), - ('approval_state', 'in', ['0', '1']) - ] - log1_valid_res_ids = TargetModel.log_ids.search(log1_domain).mapped('res_id') - log2_domain = log1_domain.copy() - log2_valid_res_ids = TargetModel.log_ids2.search(log2_domain).mapped('res_id') + condition2_todo_ids = [] + if target_records: + for rec in target_records: + log_domain = [ + ("model", "=", rec.res_model), + ("res_id", "=", rec.res_id), + ('done_by', '=', current_uid), + ('approval_state', 'in', ['0', '1']) + ] + has_valid_log1 = self.env['soong.workflow.log'].search_count(log_domain) > 0 + has_valid_log2 = self.env['soong.workflow.log2'].search_count(log_domain) > 0 - valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) - for rec_id, (model, res_id) in rec_model_res_map.items(): - if model == model_name and res_id in valid_res_ids: - condition2_invalid_ids.append(rec_id) + if has_valid_log1 or has_valid_log2: + condition2_todo_ids.append(rec.id) - invalid_ids = list(set(condition1_invalid_ids + condition2_invalid_ids)) - domain = [('id', 'not in', invalid_ids)] + todo_ids = list(set(condition1_todo_ids + condition2_todo_ids)) + # 非待办 = 基础候选记录 - 待办记录 + all_candidate_ids = candidate_records.ids + non_todo_ids = [rec_id for rec_id in all_candidate_ids if rec_id not in todo_ids] + domain = [('id', 'in', non_todo_ids)] if non_todo_ids else [('id', '=', False)] else: + # 不支持的操作符 domain = [('id', '=', False)] return domain @@ -669,90 +627,13 @@ class SoongWorkflowRecord(models.Model): @api.model def get_view(self, view_id=None, view_type='form', **options): - """优化后:批量查询+批量写入,消除嵌套循环""" - # ========== 第一步:批量查询所有目标记录(仅1次搜索) ========== current_user_id = self.env.user.id all_records = self.search([ ('users_ids', 'in', current_user_id), - ('soong_state', '!=', 'active'), + ('reclaim_state', '=', False) ]) if not all_records: return super().get_view(view_id, view_type, **options) - - model_res_ids = defaultdict(list) - rec_model_res_map = {rec.id: (rec.res_model, rec.res_id) for rec in all_records if rec.res_model and rec.res_id} - for rec_id, (model, res_id) in rec_model_res_map.items(): - model_res_ids[model].append(res_id) - - log_data = defaultdict(dict) - for model_name, res_ids in model_res_ids.items(): - if not res_ids: - continue - try: - TargetModel = self.env[model_name].sudo() - has_log1 = 'log_ids' in TargetModel._fields - has_log2 = 'log_ids2' in TargetModel._fields - - # 批量查log_ids(当前用户,仅当字段存在时) - if has_log1: - log1_domain = [('res_id', 'in', res_ids), ('done_by', '=', current_user_id)] - log1_records = TargetModel.log_ids.search(log1_domain) - for log in log1_records: - if log.res_id not in log_data[model_name]: - log_data[model_name][log.res_id] = {'log1_states': [], 'log2_states': [], - 'has_archive_log': False} - log_data[model_name][log.res_id]['log1_states'].append(log.approval_state) - if log.name == '归档': - log_data[model_name][log.res_id]['has_archive_log'] = True - log_data[model_name][log.res_id]['archive_state'] = log.approval_state - - # 批量查log_ids2(当前用户,仅当字段存在时) - if has_log2: - log2_domain = [('res_id', 'in', res_ids), ('done_by.name', '=', self.env.user.name)] - log2_records = TargetModel.log_ids2.search(log2_domain) - for log2 in log2_records: - if log2.res_id not in log_data[model_name]: - log_data[model_name][log2.res_id] = {'log1_states': [], 'log2_states': [], - 'has_archive_log': False} - log_data[model_name][log2.res_id]['log2_states'].append(log2.approval_state) - except Exception: - continue - - to_write = defaultdict(list) # {状态: [记录ID]} - for rec in all_records: - if rec.id not in rec_model_res_map: - continue - model, res_id = rec_model_res_map[rec.id] - if model not in log_data or res_id not in log_data[model]: - continue - - log_info = log_data[model][res_id] - # 处理归档日志优先级最高 - if log_info['has_archive_log']: - if log_info['archive_state'] in ['0', '1']: - to_write['to_active'].append(rec.id) - elif log_info['archive_state'] == '2': - to_write['active'].append(rec.id) - continue - - # 处理普通log_ids - log1_states = log_info['log1_states'] - if any(s in ['0', '1'] for s in log1_states): - to_write['to_do'].append(rec.id) - elif all(s == '2' for s in log1_states if s): - to_write['done'].append(rec.id) - - # 处理log_ids2 - log2_states = log_info['log2_states'] - if log2_states: - if any(s in ['0', '1'] for s in log2_states): - to_write['to_do'].append(rec.id) - elif all(s == '2' for s in log2_states): - to_write['done'].append(rec.id) - - for state, rec_ids in to_write.items(): - if rec_ids: - self.browse(rec_ids).write({'soong_state': state}) all_records._compute_is_done() all_records._compute_is_to_do() return super().get_view(view_id, view_type, **options) From 43a66272c3cf05f6dbef47a6837308c16b671d56 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: Fri, 20 Mar 2026 17:33:33 +0800 Subject: [PATCH 02/19] =?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_record.py | 2 -- soong_workflow/wizard/soong_workflow_comment_wizard.py | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 6ca409b45..c096933b0 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -201,10 +201,8 @@ class SoongWorkflowRecord(models.Model): # 第一步:基础过滤 - 只处理包含当前用户的记录 base_domain = [('users_ids', 'in', [current_uid])] candidate_records = self.search(base_domain) - if not candidate_records: return [('id', '=', False)] - # 处理 "is_to_do = True" 或 "is_to_do != False" 的情况 if (operator == '=' and value) or (operator == '!=' and not value): # 条件1:approve_state为start的记录ID diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index d9d074812..15841563e 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -386,8 +386,10 @@ class SoongWorkflowCommentWizard(models.TransientModel): 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)] + 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), + ('role_group_ids', 'in', link_users_group_ids.ids) + ] if user_role_domain: role_domain = ['|'] + role_domain + user_role_domain domain = role_domain + company_domain + department_domain From ee48f814fc2d1caf7413c7dd7950dd9541ff9dea 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: Fri, 20 Mar 2026 18:05:12 +0800 Subject: [PATCH 03/19] =?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 --- .../models/soong_workflow_record.py | 199 +++++++++++------- 1 file changed, 121 insertions(+), 78 deletions(-) diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index c096933b0..7f0e22190 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -169,111 +169,154 @@ class SoongWorkflowRecord(models.Model): _logger.warning(f"Failed to get urgency_level from {record.res_model}: {e}") record.urgency_level = 'normal' - @api.depends('employee_ids', 'res_model', 'res_id', 'soong_state', 'reclaim_state', 'approve_state', 'users_ids') + @api.depends('employee_ids', 'res_model', 'res_id', 'soong_state', 'reclaim_state', 'approve_state') def _compute_is_to_do(self): + """保留原有计算逻辑(已优化批量查询,参考之前的版本)""" current_uid = self.env.user.id - for rec in self: - rec.is_to_do = False - valid_records = self.filtered( - lambda r: current_uid in r.users_ids.ids + current_employees = self.env['hr.employee'].search([('user_id', '=', current_uid)]) + active_employee_ids = current_employees.ids if current_employees else [] + if not active_employee_ids: + self.is_to_do = False + return + all_employees = self.env['hr.employee'].search([('user_state', '=', 'in')]) + emp_to_user = {emp.id: emp.user_id.id for emp in all_employees if emp.user_id} + + # 第一步:批量处理条件1(approve_state=start) + condition1_records = self.filtered( + lambda r: not r.reclaim_state and r.approve_state == 'start' and + set(active_employee_ids) & set(r.employee_ids.ids + r.employee2_ids.ids) ) - if not valid_records: - return - start_records = valid_records.filtered(lambda r: r.approve_state == 'start') - start_records.is_to_do = True - remaining_records = valid_records - start_records - if not remaining_records: - return - for rec in remaining_records: + condition1_records.is_to_do = True + + # 第二步:批量处理条件2(log_ids/log_ids2) + remaining_records = self - condition1_records + remaining_records.is_to_do = False + + # 按res_model分组批量查询log(核心优化) + model_res_ids = defaultdict(list) + rec_model_res_map = {} + for rec in remaining_records.filtered(lambda r: not r.reclaim_state and r.res_model and r.res_id): + model_res_ids[rec.res_model].append(rec.res_id) + rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) + + # 批量查log满足条件的记录ID + valid_rec_ids = [] + for model_name, res_ids in model_res_ids.items(): + if not res_ids: + continue + TargetModel = self.env[model_name].sudo() log_domain = [ - ("model", "=", rec.res_model ), - ("res_id", "=", rec.res_id), - ('done_by', '=', current_uid), + ('res_id', 'in', res_ids), + ('done_by', 'in', list(emp_to_user.values())), ('approval_state', 'in', ['0', '1']) ] - has_valid_log1 = self.env['soong.workflow.log'].search_count(log_domain) > 0 - has_valid_log2 = self.env['soong.workflow.log2'].search_count(log_domain) > 0 - if has_valid_log1 or has_valid_log2: - rec.is_to_do = True + # 批量查log_ids + log1_valid_res_ids = TargetModel.log_ids.search(log_domain).mapped('res_id') + # 批量查log_ids2 + log2_valid_res_ids = TargetModel.log_ids2.search(log_domain).mapped('res_id') + + valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) + for rec_id, (model, res_id) in rec_model_res_map.items(): + if model == model_name and res_id in valid_res_ids: + valid_rec_ids.append(rec_id) + self.browse(valid_rec_ids).is_to_do = True def _search_is_to_do(self, operator, value): + """搜索待办状态:仅打印总耗时""" + current_uid = self.env.user.id - # 第一步:基础过滤 - 只处理包含当前用户的记录 - base_domain = [('users_ids', 'in', [current_uid])] - candidate_records = self.search(base_domain) - if not candidate_records: + current_employees = self.env['hr.employee'].sudo().search([('user_id', '=', current_uid)]) + active_employee_ids = current_employees.ids if current_employees else [] + if not active_employee_ids: return [('id', '=', False)] - # 处理 "is_to_do = True" 或 "is_to_do != False" 的情况 + + base_domain = [ + ('reclaim_state', '=', False), + '|', + ('employee_ids', 'in', active_employee_ids), + ('employee2_ids', 'in', active_employee_ids) + ] + if (operator == '=' and value) or (operator == '!=' and not value): - # 条件1:approve_state为start的记录ID + candidate_records = self.search(base_domain) + if not candidate_records: + return [('id', '=', False)] + condition1_ids = candidate_records.filtered( - lambda r: r.approve_state == 'start' + lambda r: r.approve_state == 'start' and not r.reclaim_state ).ids - # 条件2:非start状态,且日志符合条件的记录ID - remaining_records = candidate_records - self.browse(condition1_ids) - # 仅筛选有res_model/res_id且非草稿/非start的记录 - target_records = remaining_records.filtered( - lambda r: r.res_model and r.res_id and r.approve_state not in ['draft', 'start'] - ) + model_res_ids = defaultdict(list) + rec_model_res_map = {} + for rec in candidate_records - self.browse(condition1_ids): + if rec.res_model and rec.res_id: + model_res_ids[rec.res_model].append(rec.res_id) + rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) condition2_rec_ids = [] - if target_records: - # 逐条检查日志(完全对齐compute逻辑,移除try-except) - for rec in target_records: - # 日志筛选条件(和compute逻辑完全一致) - log_domain = [ - ("model", "=", rec.res_model), - ("res_id", "=", rec.res_id), - ('done_by', '=', current_uid), - ('approval_state', 'in', ['0', '1']) - ] - # 直接查询soong.workflow.log和log2模型 - has_valid_log1 = self.env['soong.workflow.log'].search_count(log_domain) > 0 - has_valid_log2 = self.env['soong.workflow.log2'].search_count(log_domain) > 0 + for model_name, res_ids in model_res_ids.items(): + if not res_ids: + continue + TargetModel = self.env[model_name].sudo() + log1_domain = [ + ('res_id', 'in', res_ids), + ('done_by', '=', current_uid), + ('approval_state', 'in', ['0', '1']) + ] + log1_valid_res_ids = TargetModel.log_ids.search(log1_domain).mapped('res_id') + log2_domain = [ + ('res_id', 'in', res_ids), + ('done_by', '=', current_uid), + ('approval_state', 'in', ['0', '1']) + ] + log2_valid_res_ids = TargetModel.log_ids2.search(log2_domain).mapped('res_id') - if has_valid_log1 or has_valid_log2: - condition2_rec_ids.append(rec.id) + valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) + for rec_id, (model, res_id) in rec_model_res_map.items(): + if model == model_name and res_id in valid_res_ids: + condition2_rec_ids.append(rec_id) - # 合并符合条件的记录ID valid_ids = list(set(condition1_ids + condition2_rec_ids)) - domain = [('id', 'in', valid_ids)] if valid_ids else [('id', '=', False)] + domain = [('id', 'in', valid_ids)] elif (operator == '=' and not value) or (operator == '!=' and value): - todo_ids = [] - # 条件1:approve_state为start的待办ID - condition1_todo_ids = candidate_records.filtered( - lambda r: r.approve_state == 'start' + candidate_records = self.search(base_domain) + if not candidate_records: + return [('id', '=', False)] + + condition1_invalid_ids = candidate_records.filtered( + lambda r: r.approve_state == 'start' and not r.reclaim_state ).ids - # 条件2:日志符合条件的待办ID - remaining_records = candidate_records - self.browse(condition1_todo_ids) - target_records = remaining_records.filtered( - lambda r: r.res_model and r.res_id and r.approve_state not in ['draft', 'start'] - ) + model_res_ids = defaultdict(list) + rec_model_res_map = {} + for rec in candidate_records - self.browse(condition1_invalid_ids): + if rec.res_model and rec.res_id: + model_res_ids[rec.res_model].append(rec.res_id) + rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) - condition2_todo_ids = [] - if target_records: - for rec in target_records: - log_domain = [ - ("model", "=", rec.res_model), - ("res_id", "=", rec.res_id), - ('done_by', '=', current_uid), - ('approval_state', 'in', ['0', '1']) - ] - has_valid_log1 = self.env['soong.workflow.log'].search_count(log_domain) > 0 - has_valid_log2 = self.env['soong.workflow.log2'].search_count(log_domain) > 0 + condition2_invalid_ids = [] + for model_name, res_ids in model_res_ids.items(): + if not res_ids: + continue + TargetModel = self.env[model_name].sudo() + log1_domain = [ + ('res_id', 'in', res_ids), + ('done_by', '=', current_uid), + ('approval_state', 'in', ['0', '1']) + ] + log1_valid_res_ids = TargetModel.log_ids.search(log1_domain).mapped('res_id') + log2_domain = log1_domain.copy() + log2_valid_res_ids = TargetModel.log_ids2.search(log2_domain).mapped('res_id') - if has_valid_log1 or has_valid_log2: - condition2_todo_ids.append(rec.id) + valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) + for rec_id, (model, res_id) in rec_model_res_map.items(): + if model == model_name and res_id in valid_res_ids: + condition2_invalid_ids.append(rec_id) - todo_ids = list(set(condition1_todo_ids + condition2_todo_ids)) - # 非待办 = 基础候选记录 - 待办记录 - all_candidate_ids = candidate_records.ids - non_todo_ids = [rec_id for rec_id in all_candidate_ids if rec_id not in todo_ids] - domain = [('id', 'in', non_todo_ids)] if non_todo_ids else [('id', '=', False)] + invalid_ids = list(set(condition1_invalid_ids + condition2_invalid_ids)) + domain = [('id', 'not in', invalid_ids)] else: - # 不支持的操作符 domain = [('id', '=', False)] return domain From 61caa779c18c6dac0777e21d0e88b9533cfbd76a 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: Fri, 20 Mar 2026 18:05:52 +0800 Subject: [PATCH 04/19] =?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_record.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 7f0e22190..56afd43b6 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -671,7 +671,7 @@ class SoongWorkflowRecord(models.Model): current_user_id = self.env.user.id all_records = self.search([ ('users_ids', 'in', current_user_id), - ('reclaim_state', '=', False) + ('soong_state', '!=', 'active'), ]) if not all_records: return super().get_view(view_id, view_type, **options) From 14ed332d544d5643f5a8feac29b22a36762bb8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 20 Mar 2026 18:06:36 +0800 Subject: [PATCH 05/19] =?UTF-8?q?=E6=8A=BC=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/deposit_month_received_report.py | 78 +++++++++--------- .../models/deposit_pre_received_report.py | 79 ++++++++++--------- 2 files changed, 85 insertions(+), 72 deletions(-) diff --git a/yuthon_report/models/deposit_month_received_report.py b/yuthon_report/models/deposit_month_received_report.py index a89bf9624..0952d0866 100644 --- a/yuthon_report/models/deposit_month_received_report.py +++ b/yuthon_report/models/deposit_month_received_report.py @@ -67,48 +67,54 @@ class DepositMonthReceivedReport(models.Model): rows = [] total_amount = 0.0 idx = 1 + processed_receivable_ids = set() for line in modify_lines: if not line.receivable_id: continue + receivable_id = line.receivable_id.id + if receivable_id in processed_receivable_ids: + continue + processed_receivable_ids.add(receivable_id) rec = line.receivable_id - # 使用receivable.modify.line的delivered1字段作为金额 - amount = round(line.delivered1 or 0.0, 2) - # 获取收款日期 - payout_date = rec.payout_date - if not payout_date: - continue - # 检查日期是否在本月范围内 - if period_start <= payout_date <= period_end: - total_amount += amount - rows.append({ - "序号": idx, - "合同编号": { - 'model': 'property.lease.contract', - 'id': rec.contract_id.id, - 'display_name': rec.contract_id.contract_code, - } if rec.contract_id else "", - "物业名称": { - 'model': 'yuthon.property', - 'id': rec.contract_id.lease_ids[0].address_id.id, - 'display_name': rec.property_name, - } if rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "", - "承租方名称": { - 'model': 'property.tenant.information', - 'id': rec.lessee_id.id, - 'display_name': rec.lessee_id.name, - } if rec.lessee_id else "", - "收款日期": payout_date.strftime("%Y-%m-%d"), - "金额": amount, - "所属公司": rec.company_id.name if rec.company_id else "", - "管理员": { - 'model': 'res.users', - 'id': rec.admin_id.id, - 'display_name': rec.admin_id.name, - } if rec.admin_id else "", - }) - idx += 1 + wizards = self.env['penalty.line.wizard'].search( + [('receivable_id', '=', receivable_id)] + ) + for wizard in wizards: + for pay in wizard.pay_line_ids: + if not pay.date: + continue + if period_start <= pay.date <= period_end: + amount = round(pay.amount, 2) + total_amount += amount + rows.append({ + "序号": idx, + "合同编号": { + 'model': 'property.lease.contract', + 'id': rec.contract_id.id, + 'display_name': rec.contract_id.contract_code, + } if rec.contract_id else "", + "物业名称": { + 'model': 'yuthon.property', + 'id': rec.contract_id.lease_ids[0].address_id.id, + 'display_name': rec.property_name, + } if rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "", + "承租方名称": { + 'model': 'property.tenant.information', + 'id': rec.lessee_id.id, + 'display_name': rec.lessee_id.name, + } if rec.lessee_id else "", + "收款日期": pay.date.strftime("%Y-%m-%d"), + "金额": amount, + "所属公司": rec.company_id.name if rec.company_id else "", + "管理员": { + 'model': 'res.users', + 'id': rec.admin_id.id, + 'display_name': rec.admin_id.name, + } if rec.admin_id else "", + }) + idx += 1 stats = [ {'name': '记录总数', 'amount': len(rows)}, diff --git a/yuthon_report/models/deposit_pre_received_report.py b/yuthon_report/models/deposit_pre_received_report.py index 4bc96324e..4ea96fddf 100644 --- a/yuthon_report/models/deposit_pre_received_report.py +++ b/yuthon_report/models/deposit_pre_received_report.py @@ -64,48 +64,55 @@ class DepositPreReceivedReport(models.Model): rows = [] total_amount = 0.0 idx = 1 + # 用于去重,同一应收账单关联的 wizard 只处理一次 + processed_receivable_ids = set() for line in modify_lines: if not line.receivable_id: continue + receivable_id = line.receivable_id.id + if receivable_id in processed_receivable_ids: + continue + processed_receivable_ids.add(receivable_id) rec = line.receivable_id - # 使用receivable.modify.line的delivered1字段作为金额 - amount = round(line.delivered1 or 0.0, 2) - # 获取收款日期 - payout_date = rec.payout_date - if not payout_date: - continue - # 检查日期是否在前期范围内 - if period_start <= payout_date <= period_end: - total_amount += amount - rows.append({ - "序号": idx, - "合同编号": { - 'model': 'property.lease.contract', - 'id': rec.contract_id.id, - 'display_name': rec.contract_id.contract_code, - } if rec.contract_id else "", - "物业名称": { - 'model': 'yuthon.property', - 'id': rec.contract_id.lease_ids[0].address_id.id, - 'display_name': rec.property_name, - } if rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "", - "承租方名称": { - 'model': 'property.tenant.information', - 'id': rec.lessee_id.id, - 'display_name': rec.lessee_id.name, - } if rec.lessee_id else "", - "收款日期": payout_date.strftime("%Y-%m-%d"), - "金额": amount, - "所属公司": rec.company_id.name if rec.company_id else "", - "管理员": { - 'model': 'res.users', - 'id': rec.admin_id.id, - 'display_name': rec.admin_id.name, - } if rec.admin_id else "", - }) - idx += 1 + wizards = self.env['penalty.line.wizard'].search( + [('receivable_id', '=', receivable_id)] + ) + for wizard in wizards: + for pay in wizard.pay_line_ids: + if not pay.date: + continue + if period_start <= pay.date <= period_end: + amount = round(pay.amount, 2) + total_amount += amount + rows.append({ + "序号": idx, + "合同编号": { + 'model': 'property.lease.contract', + 'id': rec.contract_id.id, + 'display_name': rec.contract_id.contract_code, + } if rec.contract_id else "", + "物业名称": { + 'model': 'yuthon.property', + 'id': rec.contract_id.lease_ids[0].address_id.id, + 'display_name': rec.property_name, + } if rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "", + "承租方名称": { + 'model': 'property.tenant.information', + 'id': rec.lessee_id.id, + 'display_name': rec.lessee_id.name, + } if rec.lessee_id else "", + "收款日期": pay.date.strftime("%Y-%m-%d"), + "金额": amount, + "所属公司": rec.company_id.name if rec.company_id else "", + "管理员": { + 'model': 'res.users', + 'id': rec.admin_id.id, + 'display_name': rec.admin_id.name, + } if rec.admin_id else "", + }) + idx += 1 stats = [ {'name': '记录总数', 'amount': len(rows)}, From 259f4af08d0955e0287f9d51954d3b89d7d29722 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: Fri, 20 Mar 2026 18:30:03 +0800 Subject: [PATCH 06/19] =?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 --- yuthon_issue/models/inherit_room_booking.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/yuthon_issue/models/inherit_room_booking.py b/yuthon_issue/models/inherit_room_booking.py index 4ae47459e..197e77dba 100644 --- a/yuthon_issue/models/inherit_room_booking.py +++ b/yuthon_issue/models/inherit_room_booking.py @@ -237,6 +237,19 @@ class IssueMeetingLine(models.Model): decision_results = fields.Selection([('agree', '通过'), ('disagree', '重新上会'), ('rejected', '驳回')], string="决策结果") decision_details = fields.Text(string="决策详情") + @api.onchange('issue_library_id') + def onchange_issue_library_id(self): + if self.issue_library_id: + dept_manager = self.env['hr.employee'].search([ + ('active', '=', True), + ('user_role_id.name', '=', '部门经理'), + '|', + ('department_id', '=', self.issue_library_id.department_id.id), + ('department_ids', 'in', [self.issue_library_id.department_id.id]) + ], limit=1) + if dept_manager: + self.employee_id = dept_manager.id + def open_record(self): form_id = self.env.ref('yuthon_issue.view_issue_library_form').id return { From 354441f8fe8b4ada8c9e66ffc7b6bb272599540c 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: Fri, 20 Mar 2026 18:37:06 +0800 Subject: [PATCH 07/19] =?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 --- .../models/soong_workflow_record.py | 231 +++++++++--------- 1 file changed, 114 insertions(+), 117 deletions(-) diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 56afd43b6..b5e69a966 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -169,153 +169,145 @@ class SoongWorkflowRecord(models.Model): _logger.warning(f"Failed to get urgency_level from {record.res_model}: {e}") record.urgency_level = 'normal' - @api.depends('employee_ids', 'res_model', 'res_id', 'soong_state', 'reclaim_state', 'approve_state') + @api.depends('employee_ids', 'res_model', 'res_id', 'soong_state', 'reclaim_state', 'approve_state', + 'employee2_ids') def _compute_is_to_do(self): - """保留原有计算逻辑(已优化批量查询,参考之前的版本)""" current_uid = self.env.user.id - current_employees = self.env['hr.employee'].search([('user_id', '=', current_uid)]) - active_employee_ids = current_employees.ids if current_employees else [] - if not active_employee_ids: - self.is_to_do = False + # 关键修改:不再查users_ids,改为查当前用户对应的hr.employee记录 + current_employee = self.env['hr.employee'].search([('user_id', '=', current_uid)], limit=1) + if not current_employee: + # 若当前用户无对应员工记录,所有记录均为非待办 + for rec in self: + rec.is_to_do = False return - all_employees = self.env['hr.employee'].search([('user_state', '=', 'in')]) - emp_to_user = {emp.id: emp.user_id.id for emp in all_employees if emp.user_id} - - # 第一步:批量处理条件1(approve_state=start) - condition1_records = self.filtered( - lambda r: not r.reclaim_state and r.approve_state == 'start' and - set(active_employee_ids) & set(r.employee_ids.ids + r.employee2_ids.ids) + # 初始化所有记录为非待办(上下文流畅,保留原有初始化逻辑) + self.is_to_do = False + valid_records = self.filtered( + lambda r: (current_employee.id in r.employee_ids.ids or current_employee.id in r.employee2_ids.ids) + and not r.reclaim_state ) - condition1_records.is_to_do = True + if not valid_records: + return + # 以下逻辑完全不变,和你原来一致 + start_records = valid_records.filtered(lambda r: r.approve_state == 'start') + start_records.is_to_do = True + remaining_records = valid_records - start_records + if not remaining_records: + return - # 第二步:批量处理条件2(log_ids/log_ids2) - remaining_records = self - condition1_records - remaining_records.is_to_do = False + target_records = remaining_records.filtered( + lambda r: r.res_model and r.res_id and r.approve_state not in ['draft', 'start'] + ) + if not target_records: + return - # 按res_model分组批量查询log(核心优化) - model_res_ids = defaultdict(list) - rec_model_res_map = {} - for rec in remaining_records.filtered(lambda r: not r.reclaim_state and r.res_model and r.res_id): - model_res_ids[rec.res_model].append(rec.res_id) - rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) + # 批量查询日志(性能优化保留,逻辑不变) + model_res_id_pairs = [(rec.res_model, rec.res_id) for rec in target_records] + log1_domain = [ + ('model', 'in', list({m for m, _ in model_res_id_pairs})), + ('res_id', 'in', list({r for _, r in model_res_id_pairs})), + ('done_by', '=', current_uid), + ('approval_state', 'in', ['0', '1']) + ] + log2_domain = log1_domain.copy() - # 批量查log满足条件的记录ID - valid_rec_ids = [] - for model_name, res_ids in model_res_ids.items(): - if not res_ids: - continue - TargetModel = self.env[model_name].sudo() - log_domain = [ - ('res_id', 'in', res_ids), - ('done_by', 'in', list(emp_to_user.values())), - ('approval_state', 'in', ['0', '1']) - ] - # 批量查log_ids - log1_valid_res_ids = TargetModel.log_ids.search(log_domain).mapped('res_id') - # 批量查log_ids2 - log2_valid_res_ids = TargetModel.log_ids2.search(log_domain).mapped('res_id') + valid_log1_pairs = set() + log1_records = self.env['soong.workflow.log'].search(log1_domain) + for log in log1_records: + valid_log1_pairs.add((log.model, log.res_id)) - valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) - for rec_id, (model, res_id) in rec_model_res_map.items(): - if model == model_name and res_id in valid_res_ids: - valid_rec_ids.append(rec_id) - self.browse(valid_rec_ids).is_to_do = True + valid_log2_pairs = set() + log2_records = self.env['soong.workflow.log2'].search(log2_domain) + for log in log2_records: + valid_log2_pairs.add((log.model, log.res_id)) + + valid_pairs = valid_log1_pairs | valid_log2_pairs + for rec in target_records: + if (rec.res_model, rec.res_id) in valid_pairs: + rec.is_to_do = True def _search_is_to_do(self, operator, value): - """搜索待办状态:仅打印总耗时""" - current_uid = self.env.user.id - current_employees = self.env['hr.employee'].sudo().search([('user_id', '=', current_uid)]) - active_employee_ids = current_employees.ids if current_employees else [] - if not active_employee_ids: + # 关键修改:和compute逻辑一致,查当前用户对应的员工,再匹配employee_ids/employee2_ids + current_employee = self.env['hr.employee'].search([('user_id', '=', current_uid)], limit=1) + if not current_employee: return [('id', '=', False)] + # 基础过滤:当前员工在employee_ids或employee2_ids中 + reclaim_state=False(和compute完全对齐) base_domain = [ - ('reclaim_state', '=', False), '|', - ('employee_ids', 'in', active_employee_ids), - ('employee2_ids', 'in', active_employee_ids) + ('employee_ids', 'in', [current_employee.id]), + ('employee2_ids', 'in', [current_employee.id]), + ('reclaim_state', '=', False) ] + candidate_records = self.search(base_domain) + if not candidate_records: + return [('id', '=', False)] + # 以下所有逻辑完全不变,和你原来一致 if (operator == '=' and value) or (operator == '!=' and not value): - candidate_records = self.search(base_domain) - if not candidate_records: - return [('id', '=', False)] - condition1_ids = candidate_records.filtered( - lambda r: r.approve_state == 'start' and not r.reclaim_state + lambda r: r.approve_state == 'start' ).ids - model_res_ids = defaultdict(list) - rec_model_res_map = {} - for rec in candidate_records - self.browse(condition1_ids): - if rec.res_model and rec.res_id: - model_res_ids[rec.res_model].append(rec.res_id) - rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) + remaining_records = candidate_records - self.browse(condition1_ids) + target_records = remaining_records.filtered( + lambda r: r.res_model and r.res_id and r.approve_state not in ['draft', 'start'] + ) condition2_rec_ids = [] - for model_name, res_ids in model_res_ids.items(): - if not res_ids: - continue - TargetModel = self.env[model_name].sudo() - log1_domain = [ - ('res_id', 'in', res_ids), + if target_records: + model_res_id_pairs = [(rec.res_model, rec.res_id) for rec in target_records] + log_domain = [ + ('model', 'in', list({m for m, _ in model_res_id_pairs})), + ('res_id', 'in', list({r for _, r in model_res_id_pairs})), ('done_by', '=', current_uid), ('approval_state', 'in', ['0', '1']) ] - log1_valid_res_ids = TargetModel.log_ids.search(log1_domain).mapped('res_id') - log2_domain = [ - ('res_id', 'in', res_ids), - ('done_by', '=', current_uid), - ('approval_state', 'in', ['0', '1']) - ] - log2_valid_res_ids = TargetModel.log_ids2.search(log2_domain).mapped('res_id') + log1_valid_pairs = set( + (log.model, log.res_id) for log in self.env['soong.workflow.log'].search(log_domain)) + log2_valid_pairs = set( + (log.model, log.res_id) for log in self.env['soong.workflow.log2'].search(log_domain)) + valid_pairs = log1_valid_pairs | log2_valid_pairs - valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) - for rec_id, (model, res_id) in rec_model_res_map.items(): - if model == model_name and res_id in valid_res_ids: - condition2_rec_ids.append(rec_id) + for rec in target_records: + if (rec.res_model, rec.res_id) in valid_pairs: + condition2_rec_ids.append(rec.id) valid_ids = list(set(condition1_ids + condition2_rec_ids)) - domain = [('id', 'in', valid_ids)] + domain = [('id', 'in', valid_ids)] if valid_ids else [('id', '=', False)] elif (operator == '=' and not value) or (operator == '!=' and value): - candidate_records = self.search(base_domain) - if not candidate_records: - return [('id', '=', False)] + condition1_todo_ids = candidate_records.filtered(lambda r: r.approve_state == 'start').ids - condition1_invalid_ids = candidate_records.filtered( - lambda r: r.approve_state == 'start' and not r.reclaim_state - ).ids + remaining_records = candidate_records - self.browse(condition1_todo_ids) + target_records = remaining_records.filtered( + lambda r: r.res_model and r.res_id and r.approve_state not in ['draft', 'start'] + ) - model_res_ids = defaultdict(list) - rec_model_res_map = {} - for rec in candidate_records - self.browse(condition1_invalid_ids): - if rec.res_model and rec.res_id: - model_res_ids[rec.res_model].append(rec.res_id) - rec_model_res_map[rec.id] = (rec.res_model, rec.res_id) - - condition2_invalid_ids = [] - for model_name, res_ids in model_res_ids.items(): - if not res_ids: - continue - TargetModel = self.env[model_name].sudo() - log1_domain = [ - ('res_id', 'in', res_ids), + condition2_todo_ids = [] + if target_records: + model_res_id_pairs = [(rec.res_model, rec.res_id) for rec in target_records] + log_domain = [ + ('model', 'in', list({m for m, _ in model_res_id_pairs})), + ('res_id', 'in', list({r for _, r in model_res_id_pairs})), ('done_by', '=', current_uid), ('approval_state', 'in', ['0', '1']) ] - log1_valid_res_ids = TargetModel.log_ids.search(log1_domain).mapped('res_id') - log2_domain = log1_domain.copy() - log2_valid_res_ids = TargetModel.log_ids2.search(log2_domain).mapped('res_id') + log1_valid_pairs = set( + (log.model, log.res_id) for log in self.env['soong.workflow.log'].search(log_domain)) + log2_valid_pairs = set( + (log.model, log.res_id) for log in self.env['soong.workflow.log2'].search(log_domain)) + valid_pairs = log1_valid_pairs | log2_valid_pairs - valid_res_ids = set(log1_valid_res_ids + log2_valid_res_ids) - for rec_id, (model, res_id) in rec_model_res_map.items(): - if model == model_name and res_id in valid_res_ids: - condition2_invalid_ids.append(rec_id) + for rec in target_records: + if (rec.res_model, rec.res_id) in valid_pairs: + condition2_todo_ids.append(rec.id) - invalid_ids = list(set(condition1_invalid_ids + condition2_invalid_ids)) - domain = [('id', 'not in', invalid_ids)] + todo_ids = list(set(condition1_todo_ids + condition2_todo_ids)) + all_candidate_ids = candidate_records.ids + non_todo_ids = [rec_id for rec_id in all_candidate_ids if rec_id not in todo_ids] + domain = [('id', 'in', non_todo_ids)] if non_todo_ids else [('id', '=', False)] else: domain = [('id', '=', False)] @@ -669,15 +661,20 @@ class SoongWorkflowRecord(models.Model): @api.model def get_view(self, view_id=None, view_type='form', **options): current_user_id = self.env.user.id - all_records = self.search([ - ('users_ids', 'in', current_user_id), - ('soong_state', '!=', 'active'), - ]) - if not all_records: + current_employee = self.env['hr.employee'].search([('user_id', '=', current_user_id)], limit=1) + if not current_employee: + return super().get_view(view_id, view_type, **options) + if view_type == 'list': + all_records = self.search([ + '|', + ('employee_ids', 'in', [current_employee.id]), + ('employee2_ids', 'in', [current_employee.id]), + ]) + if not all_records: + return super().get_view(view_id, view_type, **options) + all_records._compute_is_done() + all_records._compute_is_to_do() return super().get_view(view_id, view_type, **options) - all_records._compute_is_done() - all_records._compute_is_to_do() - return super().get_view(view_id, view_type, **options) def to_do_process_reminders(self): workflow_records = self.env['soong.workflow.record'].search([ From 9a587caba7bc7e3d26d70e1cd31da4c5bdba0517 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: Fri, 20 Mar 2026 18:41:45 +0800 Subject: [PATCH 08/19] =?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_record.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index b5e69a966..0bfb6d9d7 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -662,19 +662,20 @@ class SoongWorkflowRecord(models.Model): def get_view(self, view_id=None, view_type='form', **options): current_user_id = self.env.user.id current_employee = self.env['hr.employee'].search([('user_id', '=', current_user_id)], limit=1) + view = super().get_view(view_id, view_type, **options) if not current_employee: - return super().get_view(view_id, view_type, **options) + return view if view_type == 'list': all_records = self.search([ '|', ('employee_ids', 'in', [current_employee.id]), ('employee2_ids', 'in', [current_employee.id]), - ]) - if not all_records: - return super().get_view(view_id, view_type, **options) - all_records._compute_is_done() - all_records._compute_is_to_do() - return super().get_view(view_id, view_type, **options) + ('reclaim_state', '=', False) # 补回丢失的过滤条件 + ]) # 限制最大记录数,避免性能问题 + if all_records: + all_records._compute_is_done() + all_records._compute_is_to_do() + return view def to_do_process_reminders(self): workflow_records = self.env['soong.workflow.record'].search([ From 55902c7cba12a1aa2ccf9262f88260f0969cc725 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: Fri, 20 Mar 2026 19:00:16 +0800 Subject: [PATCH 09/19] =?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_record.py | 3 --- soong_workflow/wizard/soong_workflow_comment_wizard.py | 6 ++---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/soong_workflow/models/soong_workflow_record.py b/soong_workflow/models/soong_workflow_record.py index 0bfb6d9d7..bce7f00a4 100644 --- a/soong_workflow/models/soong_workflow_record.py +++ b/soong_workflow/models/soong_workflow_record.py @@ -228,12 +228,9 @@ class SoongWorkflowRecord(models.Model): def _search_is_to_do(self, operator, value): current_uid = self.env.user.id - # 关键修改:和compute逻辑一致,查当前用户对应的员工,再匹配employee_ids/employee2_ids current_employee = self.env['hr.employee'].search([('user_id', '=', current_uid)], limit=1) if not current_employee: return [('id', '=', False)] - - # 基础过滤:当前员工在employee_ids或employee2_ids中 + reclaim_state=False(和compute完全对齐) base_domain = [ '|', ('employee_ids', 'in', [current_employee.id]), diff --git a/soong_workflow/wizard/soong_workflow_comment_wizard.py b/soong_workflow/wizard/soong_workflow_comment_wizard.py index 15841563e..c5aad805b 100644 --- a/soong_workflow/wizard/soong_workflow_comment_wizard.py +++ b/soong_workflow/wizard/soong_workflow_comment_wizard.py @@ -386,10 +386,8 @@ class SoongWorkflowCommentWizard(models.TransientModel): 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), - ('role_group_ids', 'in', link_users_group_ids.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 From 9fc5291f08146f70aaac1a79f03b8ff48b04c663 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: Fri, 20 Mar 2026 19:09:34 +0800 Subject: [PATCH 10/19] =?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 --- yuthon_hr_employee_approval/models/yuthon_renewal_approval.py | 1 + 1 file changed, 1 insertion(+) diff --git a/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py b/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py index 202e478f8..1fdf64119 100644 --- a/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py +++ b/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py @@ -51,6 +51,7 @@ class YuthonRenewalApproval(models.Model): new_version = f"V{new_version_num:.1f}" new_contract_vals = original_contract.copy_data()[0] new_contract_type_id = original_contract.contract_type_id.id + original_contract.state = 'close' if original_contract.contract_type_id and original_contract.contract_type_id.name: contract_type_name = original_contract.contract_type_id.name if '一签' in contract_type_name: From d5f681d5678c316d0d556bde5e4ce7518fc7fccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 20 Mar 2026 19:10:26 +0800 Subject: [PATCH 11/19] =?UTF-8?q?=E6=8A=BC=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/deposit_pre_received_report.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/yuthon_report/models/deposit_pre_received_report.py b/yuthon_report/models/deposit_pre_received_report.py index 4ea96fddf..844e84d19 100644 --- a/yuthon_report/models/deposit_pre_received_report.py +++ b/yuthon_report/models/deposit_pre_received_report.py @@ -12,8 +12,7 @@ class DepositPreReceivedReport(models.Model): 押金前期实收报表: - 数据源: receivable.modify.line(应收类型为押金) - 金额: penalty.pay.line.amount(支付金额) - - 日期过滤: penalty.pay.line.date 在本年1月至查询月上个月最后一天(不含查询月) - - 查1月返回空数据 + - 日期过滤: penalty.pay.line.date 在无限制至查询月上个月最后一天(不含查询月) """ date_start = params.get("date_start") if date_start: @@ -21,19 +20,11 @@ class DepositPreReceivedReport(models.Model): else: query_date = datetime.date.today() - query_year = query_date.year query_month = query_date.month - # 查询月为1月时返回空数据 - if query_month == 1: - return { - "rows": [], - "stats": [{'name': '记录总数', 'amount': 0}, {'name': '合计金额', 'amount': 0}] - } - - # 前期范围:本年1月至1日 ~ 查询月上个月最后一天 - period_start = datetime.date(query_year, 1, 1) - period_end = datetime.date(query_year, query_month, 1) - datetime.timedelta(days=1) + # 前期范围:无限制 ~ 查询月上个月最后一天 + # 只设置结束日期,不设置开始日期,实现完全无限制 + period_end = datetime.date(query_date.year, query_month, 1) - datetime.timedelta(days=1) deposit_type = self.env['yuthon.management.type'].search([('name', '=', '押金')], limit=1) @@ -83,7 +74,7 @@ class DepositPreReceivedReport(models.Model): for pay in wizard.pay_line_ids: if not pay.date: continue - if period_start <= pay.date <= period_end: + if pay.date <= period_end: amount = round(pay.amount, 2) total_amount += amount rows.append({ From 1319ced2d66b1486f8c05d253c3a7573dab5efa9 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: Fri, 20 Mar 2026 19:18:39 +0800 Subject: [PATCH 12/19] =?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 --- .../models/yuthon_renewal_approval.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py b/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py index 1fdf64119..d4dd81ee3 100644 --- a/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py +++ b/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py @@ -51,7 +51,6 @@ class YuthonRenewalApproval(models.Model): new_version = f"V{new_version_num:.1f}" new_contract_vals = original_contract.copy_data()[0] new_contract_type_id = original_contract.contract_type_id.id - original_contract.state = 'close' if original_contract.contract_type_id and original_contract.contract_type_id.name: contract_type_name = original_contract.contract_type_id.name if '一签' in contract_type_name: @@ -64,11 +63,11 @@ class YuthonRenewalApproval(models.Model): new_contract_type_id = no_fixed_type.id new_contract_vals.update({ 'date_end': self.renewal_date, - # 'date_start': original_contract.date_end + relativedelta(days=1), + 'date_start': original_contract.date_end + relativedelta(days=1), 'state': 'open', 'job_id': self.job_id.id, 'version': new_version, - # 'new_contract_type_id': new_contract_type_id, + 'new_contract_type_id': new_contract_type_id, 'structure_type_id': self.payroll_structure_id.type_id.id, 'department_id': self.department1_id.id }) From 9a8cd05d4f65711d835d5028f1a5d403bebd8fec 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: Fri, 20 Mar 2026 19:20:35 +0800 Subject: [PATCH 13/19] =?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 --- yuthon_hr_employee_approval/models/yuthon_renewal_approval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py b/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py index d4dd81ee3..9df7ea7bf 100644 --- a/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py +++ b/yuthon_hr_employee_approval/models/yuthon_renewal_approval.py @@ -67,7 +67,7 @@ class YuthonRenewalApproval(models.Model): 'state': 'open', 'job_id': self.job_id.id, 'version': new_version, - 'new_contract_type_id': new_contract_type_id, + # 'new_contract_type_id': new_contract_type_id, 'structure_type_id': self.payroll_structure_id.type_id.id, 'department_id': self.department1_id.id }) From 5dff69e990756368e1abc57eb169fcad00252c98 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: Fri, 20 Mar 2026 19:32:22 +0800 Subject: [PATCH 14/19] =?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 --- yuthon_hr_attendance/controllers/applet_attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yuthon_hr_attendance/controllers/applet_attendance.py b/yuthon_hr_attendance/controllers/applet_attendance.py index 889422d8f..6e199785e 100644 --- a/yuthon_hr_attendance/controllers/applet_attendance.py +++ b/yuthon_hr_attendance/controllers/applet_attendance.py @@ -178,7 +178,7 @@ class AppletAttendance(Controller): check_in_str = check_in.strftime('%H:%M:%S') if check_in else ' ' check_out_str = check_out.strftime('%H:%M:%S') if check_out else ' ' has_leave_batch_workflow = False - if attendance_id.workflow_id and attendance_id.workflow_id.res_model == 'hr.leave.batch': + if attendance_id.workflow_id and attendance_id.workflow_id.res_model in ['hr.leave.batch', 'go.approval']: has_leave_batch_workflow = True check_in_html = f'

{check_in_str}

' if has_leave_batch_workflow else ( From e0a0364c7f465b254712c58c0b67feb2f24b0a2b 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: Fri, 20 Mar 2026 19:35:22 +0800 Subject: [PATCH 15/19] =?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 --- yuthon_hr_attendance/controllers/applet_attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yuthon_hr_attendance/controllers/applet_attendance.py b/yuthon_hr_attendance/controllers/applet_attendance.py index 6e199785e..6357fad50 100644 --- a/yuthon_hr_attendance/controllers/applet_attendance.py +++ b/yuthon_hr_attendance/controllers/applet_attendance.py @@ -299,7 +299,7 @@ class AppletAttendance(Controller): check_out_str = check_out.strftime('%H:%M:%S') if check_out else ' ' has_leave_batch_workflow = False if hasattr(attendance_id, 'workflow_id') and attendance_id.workflow_id: - if attendance_id.workflow_id.res_model == 'hr.leave.batch': + if attendance_id.workflow_id.res_model in ['hr.leave.batch', 'go.approval']: has_leave_batch_workflow = True check_in_html = ( From 513fbd99bc8c34aec0e03c94787859e368a68de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 20 Mar 2026 19:39:11 +0800 Subject: [PATCH 16/19] =?UTF-8?q?=E6=8A=BC=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/deposit_pre_received_report.py | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/yuthon_report/models/deposit_pre_received_report.py b/yuthon_report/models/deposit_pre_received_report.py index 844e84d19..c3efd7a14 100644 --- a/yuthon_report/models/deposit_pre_received_report.py +++ b/yuthon_report/models/deposit_pre_received_report.py @@ -55,27 +55,21 @@ class DepositPreReceivedReport(models.Model): rows = [] total_amount = 0.0 idx = 1 - # 用于去重,同一应收账单关联的 wizard 只处理一次 - processed_receivable_ids = set() for line in modify_lines: if not line.receivable_id: continue receivable_id = line.receivable_id.id - if receivable_id in processed_receivable_ids: - continue - processed_receivable_ids.add(receivable_id) rec = line.receivable_id wizards = self.env['penalty.line.wizard'].search( [('receivable_id', '=', receivable_id)] ) - for wizard in wizards: - for pay in wizard.pay_line_ids: - if not pay.date: - continue - if pay.date <= period_end: - amount = round(pay.amount, 2) + if not wizards: + # 如果没有关联的wizard,尝试直接从line获取信息 + if line.delivered1 and line.create_date: + if line.create_date.date() <= period_end: + amount = round(line.delivered1, 2) total_amount += amount rows.append({ "序号": idx, @@ -94,7 +88,7 @@ class DepositPreReceivedReport(models.Model): 'id': rec.lessee_id.id, 'display_name': rec.lessee_id.name, } if rec.lessee_id else "", - "收款日期": pay.date.strftime("%Y-%m-%d"), + "收款日期": line.create_date.strftime("%Y-%m-%d"), "金额": amount, "所属公司": rec.company_id.name if rec.company_id else "", "管理员": { @@ -104,6 +98,41 @@ class DepositPreReceivedReport(models.Model): } if rec.admin_id else "", }) idx += 1 + else: + for wizard in wizards: + for pay in wizard.pay_line_ids: + if not pay.date: + continue + if pay.date <= period_end: + amount = round(pay.amount, 2) + total_amount += amount + rows.append({ + "序号": idx, + "合同编号": { + 'model': 'property.lease.contract', + 'id': rec.contract_id.id, + 'display_name': rec.contract_id.contract_code, + } if rec.contract_id else "", + "物业名称": { + 'model': 'yuthon.property', + 'id': rec.contract_id.lease_ids[0].address_id.id, + 'display_name': rec.property_name, + } if rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "", + "承租方名称": { + 'model': 'property.tenant.information', + 'id': rec.lessee_id.id, + 'display_name': rec.lessee_id.name, + } if rec.lessee_id else "", + "收款日期": pay.date.strftime("%Y-%m-%d"), + "金额": amount, + "所属公司": rec.company_id.name if rec.company_id else "", + "管理员": { + 'model': 'res.users', + 'id': rec.admin_id.id, + 'display_name': rec.admin_id.name, + } if rec.admin_id else "", + }) + idx += 1 stats = [ {'name': '记录总数', 'amount': len(rows)}, From 3f52efe5bc240189a075112e50093d2a29663f6d 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: Fri, 20 Mar 2026 19:39:15 +0800 Subject: [PATCH 17/19] =?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 --- yuthon_hr_attendance/controllers/applet_attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yuthon_hr_attendance/controllers/applet_attendance.py b/yuthon_hr_attendance/controllers/applet_attendance.py index 6357fad50..76b3b4ac9 100644 --- a/yuthon_hr_attendance/controllers/applet_attendance.py +++ b/yuthon_hr_attendance/controllers/applet_attendance.py @@ -737,7 +737,7 @@ class AppletAttendance(Controller): has_leave_batch_workflow = False if hasattr(att, 'workflow_id') and att.workflow_id: - if att.workflow_id.res_model == 'hr.leave.batch': + if att.workflow_id.res_model in ['hr.leave.batch', 'go.approval']: has_leave_batch_workflow = True if not check_in_status and not check_out_status: From bf0c412d98c7aeacf816d23efa6886ff3d3da816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 20 Mar 2026 19:56:14 +0800 Subject: [PATCH 18/19] =?UTF-8?q?=E6=8A=BC=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_report/models/deposit_pre_received_report.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/yuthon_report/models/deposit_pre_received_report.py b/yuthon_report/models/deposit_pre_received_report.py index c3efd7a14..053f31667 100644 --- a/yuthon_report/models/deposit_pre_received_report.py +++ b/yuthon_report/models/deposit_pre_received_report.py @@ -67,8 +67,10 @@ class DepositPreReceivedReport(models.Model): ) if not wizards: # 如果没有关联的wizard,尝试直接从line获取信息 - if line.delivered1 and line.create_date: - if line.create_date.date() <= period_end: + if line.delivered1 and (line.receivable_id.payout_date or line.create_date): + # 使用payout_date作为收款日期,如果没有则使用create_date + payment_date = line.receivable_id.payout_date or line.create_date.date() + if payment_date <= period_end: amount = round(line.delivered1, 2) total_amount += amount rows.append({ @@ -88,7 +90,7 @@ class DepositPreReceivedReport(models.Model): 'id': rec.lessee_id.id, 'display_name': rec.lessee_id.name, } if rec.lessee_id else "", - "收款日期": line.create_date.strftime("%Y-%m-%d"), + "收款日期": payment_date.strftime("%Y-%m-%d"), "金额": amount, "所属公司": rec.company_id.name if rec.company_id else "", "管理员": { From a3ec4e39f4cc5ba342bad9b0af75686336e10a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 20 Mar 2026 20:12:00 +0800 Subject: [PATCH 19/19] =?UTF-8?q?=E6=8A=BC=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/deposit_month_received_report.py | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/yuthon_report/models/deposit_month_received_report.py b/yuthon_report/models/deposit_month_received_report.py index 0952d0866..b144a65f1 100644 --- a/yuthon_report/models/deposit_month_received_report.py +++ b/yuthon_report/models/deposit_month_received_report.py @@ -67,26 +67,23 @@ class DepositMonthReceivedReport(models.Model): rows = [] total_amount = 0.0 idx = 1 - processed_receivable_ids = set() for line in modify_lines: if not line.receivable_id: continue receivable_id = line.receivable_id.id - if receivable_id in processed_receivable_ids: - continue - processed_receivable_ids.add(receivable_id) rec = line.receivable_id wizards = self.env['penalty.line.wizard'].search( [('receivable_id', '=', receivable_id)] ) - for wizard in wizards: - for pay in wizard.pay_line_ids: - if not pay.date: - continue - if period_start <= pay.date <= period_end: - amount = round(pay.amount, 2) + if not wizards: + # 如果没有关联的wizard,尝试直接从line获取信息 + if line.delivered1 and (line.receivable_id.payout_date or line.create_date): + # 使用payout_date作为收款日期,如果没有则使用create_date + payment_date = line.receivable_id.payout_date or line.create_date.date() + if period_start <= payment_date <= period_end: + amount = round(line.delivered1, 2) total_amount += amount rows.append({ "序号": idx, @@ -105,7 +102,7 @@ class DepositMonthReceivedReport(models.Model): 'id': rec.lessee_id.id, 'display_name': rec.lessee_id.name, } if rec.lessee_id else "", - "收款日期": pay.date.strftime("%Y-%m-%d"), + "收款日期": payment_date.strftime("%Y-%m-%d"), "金额": amount, "所属公司": rec.company_id.name if rec.company_id else "", "管理员": { @@ -115,6 +112,41 @@ class DepositMonthReceivedReport(models.Model): } if rec.admin_id else "", }) idx += 1 + else: + for wizard in wizards: + for pay in wizard.pay_line_ids: + if not pay.date: + continue + if period_start <= pay.date <= period_end: + amount = round(pay.amount, 2) + total_amount += amount + rows.append({ + "序号": idx, + "合同编号": { + 'model': 'property.lease.contract', + 'id': rec.contract_id.id, + 'display_name': rec.contract_id.contract_code, + } if rec.contract_id else "", + "物业名称": { + 'model': 'yuthon.property', + 'id': rec.contract_id.lease_ids[0].address_id.id, + 'display_name': rec.property_name, + } if rec.contract_id and rec.contract_id.lease_ids and rec.contract_id.lease_ids[0].address_id else rec.property_name or "", + "承租方名称": { + 'model': 'property.tenant.information', + 'id': rec.lessee_id.id, + 'display_name': rec.lessee_id.name, + } if rec.lessee_id else "", + "收款日期": pay.date.strftime("%Y-%m-%d"), + "金额": amount, + "所属公司": rec.company_id.name if rec.company_id else "", + "管理员": { + 'model': 'res.users', + 'id': rec.admin_id.id, + 'display_name': rec.admin_id.name, + } if rec.admin_id else "", + }) + idx += 1 stats = [ {'name': '记录总数', 'amount': len(rows)},