diff --git a/yuthon_ai_agent/controllers/report.py b/yuthon_ai_agent/controllers/report.py index e47bc161f..99eb20520 100644 --- a/yuthon_ai_agent/controllers/report.py +++ b/yuthon_ai_agent/controllers/report.py @@ -78,7 +78,26 @@ class AiReportController(http.Controller): except json.JSONDecodeError: pass - # 2c. 如果没有模板或模板没有列配置,从 report 自身字段生成 + # 2c. 兜底:如果 report_config 没有列配置,直接从 column_ids 构建 + if not config.get('columns') and template and template.column_ids: + columns = [] + for c in template.column_ids: + fname = c.field_name or (c.field_id.name if c.field_id else '') + if not fname: + continue + columns.append({ + 'k': fname, + 'l': c.label or fname, + 'w': c.width or 120, + }) + if columns: + config['columns'] = columns + _logger.info( + '报表列配置从 column_ids 构建: report_id=%s, cols=%s', + report.id, len(columns) + ) + + # 2d. 最终兜底:从 report 自身字段生成 if not config.get('columns'): fields = json.loads(report.field_list or '[]') labels = {} @@ -89,7 +108,7 @@ class AiReportController(http.Controller): for f in fields ] else: - # 2c-bis: 兜底校验 — 如果模板 column 的 key 在数据中不存在,用数据字段重建 + # 2e. 兜底校验 — 如果模板 column 的 key 在数据中不存在,用数据字段重建 data_records = data.get('records', []) if data_records: first = data_records[0] @@ -125,7 +144,7 @@ class AiReportController(http.Controller): '模板 %s: 列已从数据字段重建,共 %s 列', template.name, len(config['columns']) ) - # 2d. 处理数据加载失败 + # 2f. 处理数据加载失败 if not data.get('success'): _logger.error( '报表页面数据加载失败: report_id=%s, token=%s, error=%s', @@ -142,7 +161,7 @@ class AiReportController(http.Controller): config['data'] = data.get('records', []) config['description'] = "共 {} 条记录".format(data.get('count', 0)) - # 2e. 子标题(数据概要) + # 2g. 子标题(数据概要) fields = data.get('fields', []) records_count = data.get('count', 0) config['subtitle'] = "{} 个字段 · {} 条记录".format(len(fields), records_count) diff --git a/yuthon_ai_agent/models/ai_report_template.py b/yuthon_ai_agent/models/ai_report_template.py index 3825c7b08..43d39dbab 100644 --- a/yuthon_ai_agent/models/ai_report_template.py +++ b/yuthon_ai_agent/models/ai_report_template.py @@ -317,7 +317,8 @@ class AiReportTemplate(models.Model): """ self.ensure_one() if self.fixed_report_id: - # 已存在:刷新数据 + # 已存在:先更新链接再刷新数据 + self.fixed_report_url_stored = self.fixed_report_id.get_report_url() result = self.fixed_report_id.refresh_data() if result.get('error'): return { @@ -329,8 +330,6 @@ class AiReportTemplate(models.Model): 'type': 'danger', } } - # 更新存储的链接字段 - self.fixed_report_url_stored = self.fixed_report_id.get_report_url() return { 'type': 'ir.actions.client', 'tag': 'display_notification', @@ -354,16 +353,16 @@ class AiReportTemplate(models.Model): 'active': True, }) self.fixed_report_id = report + # ★ 先写链接再刷新数据,避免 refresh_data 内部 search_read flush 导致赋值丢失 + url = report.get_report_url() + self.fixed_report_url_stored = url # 立即刷新数据,填充 report_data 缓存 result = report.refresh_data() if result.get('error'): _logger.error( - '固定报表创建后刷新失败: template=%s(%s), report=%s, error=%s', - self.name, self.id, report.id, result['error'] + '固定报表创建后刷新失败: template=%s(%s), report=%s, url=%s, error=%s', + self.name, self.id, report.id, url, result['error'] ) - # 报表已创建但数据刷新失败,仍返回链接但提示用户 - url = report.get_report_url() - self.fixed_report_url_stored = url return { 'type': 'ir.actions.client', 'tag': 'display_notification', @@ -374,9 +373,6 @@ class AiReportTemplate(models.Model): 'sticky': True, } } - # 存储链接到模板字段 - url = report.get_report_url() - self.fixed_report_url_stored = url _logger.info( '固定报表创建成功: template=%s(%s), report=%s, url=%s, count=%s', self.name, self.id, report.id, url, result.get('count', 0)