报表
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
||||
from odoo import models, fields, api
|
||||
from datetime import timedelta
|
||||
@@ -40,11 +41,13 @@ class AiReportTemplate(models.Model):
|
||||
string='列定义', copy=True)
|
||||
card_ids = fields.One2many('ai.report.template.card', 'template_id',
|
||||
string='汇总卡片', copy=True)
|
||||
group_by_ids = fields.One2many('ai.report.template.group.by', 'template_id',
|
||||
group_by_ids = fields.Many2many('ai.report.template.group.by',
|
||||
'ai_report_template_group_rel', 'template_id', 'group_by_id',
|
||||
string='分组设置', copy=True)
|
||||
color_rule_ids = fields.One2many('ai.report.template.color.rule', 'template_id',
|
||||
string='颜色规则', copy=True)
|
||||
filter_ids = fields.One2many('ai.report.template.filter', 'template_id',
|
||||
filter_ids = fields.Many2many('ai.report.template.filter',
|
||||
'ai_report_template_filter_rel', 'template_id', 'filter_id',
|
||||
string='筛选器', copy=True)
|
||||
param_def_ids = fields.One2many('ai.report.template.param.def', 'template_id',
|
||||
string='AI 参数定义', copy=True)
|
||||
@@ -60,8 +63,7 @@ class AiReportTemplate(models.Model):
|
||||
help='自动从参数定义行生成,也可手动编辑 JSON')
|
||||
|
||||
# ===== Computed: report_config =====
|
||||
@api.depends('column_ids', 'column_ids.color_rule_ids',
|
||||
'card_ids', 'group_by_ids',
|
||||
@api.depends('column_ids', 'card_ids', 'group_by_ids',
|
||||
'filter_ids', 'default_visible')
|
||||
def _compute_report_config(self):
|
||||
for t in self:
|
||||
@@ -89,10 +91,20 @@ class AiReportTemplate(models.Model):
|
||||
cfg['groupBy'] = gb
|
||||
cr = []
|
||||
for col in t.column_ids:
|
||||
for r in col.color_rule_ids:
|
||||
if col.color_value and col.cell_color:
|
||||
# 解析操作符前缀: >= <= != > < = ~
|
||||
cv = col.color_value.strip()
|
||||
m = re.match(r'^(>=|<=|!=|>|<|=|~)(.+)$', cv)
|
||||
if m:
|
||||
op_prefix, val = m.group(1), m.group(2)
|
||||
op_map = {'=': 'eq', '~': 'contains', '!=': 'neq',
|
||||
'>': '>', '<': '<', '>=': '>=', '<=': '<='}
|
||||
op = op_map.get(op_prefix, 'eq')
|
||||
else:
|
||||
op, val = 'eq', cv
|
||||
cr.append({
|
||||
'field': col.field_name,
|
||||
'rules': [{'op': r.op, 'value': r.value, 'color': r.color}],
|
||||
'rules': [{'op': op, 'value': val.strip(), 'color': col.cell_color}],
|
||||
})
|
||||
if cr:
|
||||
# 合并相同 field 的规则
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from odoo import models, fields, api
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -23,9 +24,18 @@ class AiReportTemplateColumn(models.Model):
|
||||
label = fields.Char('显示名称',
|
||||
help='不填则默认使用字段名')
|
||||
width = fields.Integer('列宽', default=120)
|
||||
color_value = fields.Char('匹配值',
|
||||
help='格式:[操作符]值。不写操作符默认等于。\n'
|
||||
' =已完成 / 已完成 → 等于"已完成"\n'
|
||||
' >100 → 大于 100\n'
|
||||
' <50 → 小于 50\n'
|
||||
' >=100 → 大于等于 100\n'
|
||||
' <=50 → 小于等于 50\n'
|
||||
' !=已完成 → 不等于"已完成"\n'
|
||||
' ~张三 → 包含"张三"')
|
||||
cell_color = fields.Char('显示颜色', default='#5DCAA5',
|
||||
help='匹配成功时显示的颜色,如 #E74C3C(红)、#5DCAA5(绿)')
|
||||
sequence = fields.Integer('排序', default=10)
|
||||
color_rule_ids = fields.One2many('ai.report.template.color.rule', 'column_id',
|
||||
string='颜色规则')
|
||||
|
||||
@api.onchange('field_id')
|
||||
def _onchange_field_id(self):
|
||||
@@ -34,6 +44,19 @@ class AiReportTemplateColumn(models.Model):
|
||||
if not self.label:
|
||||
self.label = self.field_id.field_description
|
||||
|
||||
@api.constrains('color_value')
|
||||
def _check_color_value(self):
|
||||
import re
|
||||
for rec in self:
|
||||
if not rec.color_value:
|
||||
continue
|
||||
# 合法格式: [>=|<=|!=|>|<|=|~]值
|
||||
if not re.match(r'^(>=|<=|!=|>|<|=|~)?\S', rec.color_value):
|
||||
raise ValidationError(
|
||||
'匹配值格式错误:%s\n'
|
||||
'正确示例:已完成、>100、<50、>=100、<=50、!=已完成、~张三' % rec.color_value
|
||||
)
|
||||
|
||||
|
||||
class AiReportTemplateCard(models.Model):
|
||||
"""报表模板 - 汇总卡片"""
|
||||
@@ -72,9 +95,7 @@ class AiReportTemplateGroupBy(models.Model):
|
||||
_description = '分组设置'
|
||||
_order = 'sequence, id'
|
||||
|
||||
template_id = fields.Many2one('ai.report.template', string='模板',
|
||||
required=True, ondelete='cascade')
|
||||
model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True)
|
||||
model_id = fields.Many2one('ir.model', string='数据模型')
|
||||
field_id = fields.Many2one('ir.model.fields', string='分组字段',
|
||||
domain="[('model_id', '=', model_id)]",
|
||||
help='选择分组字段,自动填充字段名和显示名称')
|
||||
@@ -127,9 +148,7 @@ class AiReportTemplateFilter(models.Model):
|
||||
_description = '筛选器'
|
||||
_order = 'sequence, id'
|
||||
|
||||
template_id = fields.Many2one('ai.report.template', string='模板',
|
||||
required=True, ondelete='cascade')
|
||||
model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True)
|
||||
model_id = fields.Many2one('ir.model', string='数据模型')
|
||||
field_id = fields.Many2one('ir.model.fields', string='字段',
|
||||
domain="[('model_id', '=', model_id)]",
|
||||
help='选择筛选字段,自动填充字段名和显示名称')
|
||||
|
||||
@@ -168,38 +168,18 @@
|
||||
|
||||
<group>
|
||||
<field name="group_by_ids" widget="many2many_tags"
|
||||
placeholder="添加分组字段..."/>
|
||||
domain="[('model_id','=',model_id)]"
|
||||
context="{'default_model_id': model_id}"
|
||||
placeholder="选择分组字段..."/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="filter_ids" widget="many2many_tags"
|
||||
placeholder="添加筛选器..."/>
|
||||
domain="[('model_id','=',model_id)]"
|
||||
context="{'default_model_id': model_id}"
|
||||
placeholder="选择筛选器..."/>
|
||||
</group>
|
||||
|
||||
<notebook>
|
||||
<page string="列定义" name="columns">
|
||||
<field name="column_ids" nolabel="1">
|
||||
<tree string="列定义" editable="bottom">
|
||||
<field name="model_id" column_invisible="1"/>
|
||||
<field name="field_id"
|
||||
placeholder="选择字段..."
|
||||
options="{'no_create': True}"/>
|
||||
<field name="label" placeholder="显示名称(可选)"/>
|
||||
<field name="color_rule_ids" nolabel="1"
|
||||
placeholder="设置颜色规则...">
|
||||
<tree string="颜色规则" editable="bottom">
|
||||
<field name="template_id" column_invisible="1"/>
|
||||
<field name="op"/>
|
||||
<field name="value" placeholder="匹配值"/>
|
||||
<field name="color"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
</tree>
|
||||
</field>
|
||||
<field name="width"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
<page string="汇总卡片" name="cards">
|
||||
<field name="card_ids" nolabel="1">
|
||||
<tree string="汇总卡片" editable="bottom">
|
||||
@@ -216,6 +196,22 @@
|
||||
</field>
|
||||
</page>
|
||||
|
||||
<page string="列定义" name="columns">
|
||||
<field name="column_ids" nolabel="1">
|
||||
<tree string="列定义" editable="bottom">
|
||||
<field name="model_id" column_invisible="1"/>
|
||||
<field name="field_id"
|
||||
placeholder="选择字段..."
|
||||
options="{'no_create': True}"/>
|
||||
<field name="label" placeholder="显示名称(可选)"/>
|
||||
<field name="color_value" placeholder="匹配值"/>
|
||||
<field name="cell_color" widget="color"/>
|
||||
<field name="width"/>
|
||||
<field name="sequence" widget="handle"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
|
||||
<page string="AI 参数" name="params" invisible="1">
|
||||
<field name="param_def_ids" nolabel="1">
|
||||
<tree string="参数定义" editable="bottom">
|
||||
|
||||
Reference in New Issue
Block a user