@@ -1,4 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': '模型测试',
|
||||
'summary': '',
|
||||
'description': '',
|
||||
'author': 'XSK',
|
||||
'website': '',
|
||||
'category': 'Uncategorized',
|
||||
'version': '0.1',
|
||||
'license': 'LGPL-3',
|
||||
'depends': ['base', 'web', 'mail'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/views.xml',
|
||||
],
|
||||
'assets': {
|
||||
'web.assets_backend': [
|
||||
'model_test/static/src/**/*',
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from odoo import http
|
||||
|
||||
|
||||
# class FilePreviewTest(http.Controller):
|
||||
# @http.route('/file_preview_test/file_preview_test', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/file_preview_test/file_preview_test/objects', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('file_preview_test.listing', {
|
||||
# 'root': '/file_preview_test/file_preview_test',
|
||||
# 'objects': http.request.env['file_preview_test.file_preview_test'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/file_preview_test/file_preview_test/objects/<model("file_preview_test.file_preview_test"):obj>', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('file_preview_test.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import models
|
||||
@@ -1,211 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from datetime import datetime, date, timedelta
|
||||
import string
|
||||
import random
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class ModelTestManyOne(models.Model):
|
||||
_name = 'model.test.many.one'
|
||||
_description = '测试模块'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string='字符串字段', tracking=True)
|
||||
test_id = fields.Many2one(string='多对一字段', comodel_name='model.test', required=True, ondelete='cascade')
|
||||
file_id = fields.Many2one(string='多对一文件', comodel_name='ir.attachment', tracking=True)
|
||||
file_ids = fields.Many2many(string='多对多文件', comodel_name='ir.attachment', relation='file_preview_test_many_one_rel', column1='test_id', column2='test_id_2')
|
||||
|
||||
|
||||
class ModelTest(models.Model):
|
||||
_name = 'model.test'
|
||||
_description = '测试模块'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(string='字符串字段', tracking=True)
|
||||
int_field = fields.Integer(string='整数', tracking=True)
|
||||
float_field = fields.Float(string='浮点数', tracking=True)
|
||||
date_field = fields.Date(string='日期字段', tracking=True)
|
||||
datetime_field = fields.Datetime(string='日期时间字段', tracking=True)
|
||||
state = fields.Selection(string='状态', selection=[('draft', '草稿'), ('done', '完成')], default='draft')
|
||||
file_id = fields.Many2one(string='多对一文件', comodel_name='ir.attachment')
|
||||
file_ids = fields.Many2many(string='多对多文件', comodel_name='ir.attachment', relation='file_preview_test_rel', column1='test_id', column2='test_id_2')
|
||||
text = fields.Text(string='长文本测试')
|
||||
one_to_many_field = fields.One2many(string='one2many字段', comodel_name='model.test.many.one', inverse_name='test_id', tracking=True)
|
||||
bool_test = fields.Boolean(string='布尔字段')
|
||||
selection_test = fields.Selection(string='选择字段', selection=[('1', '选项一'), ('2', '选项二')])
|
||||
|
||||
def action_generate_sample_text(self):
|
||||
"""生成长文本样例"""
|
||||
sample_text = """
|
||||
# 这是Markdown格式的样例文本
|
||||
|
||||
## 项目概述
|
||||
这是一个测试项目,用于展示Odoo中各种字段类型的功能和显示效果。
|
||||
|
||||
### 功能特点
|
||||
1. 支持多种数据类型
|
||||
2. 自动生成测试数据
|
||||
3. 便于开发和测试
|
||||
|
||||
### 技术规格
|
||||
- 数据库:PostgreSQL
|
||||
- 框架:Odoo 14.0+
|
||||
- 语言:Python/XML/JavaScript
|
||||
|
||||
---
|
||||
**注意**:此内容为自动生成,仅用于测试目的。
|
||||
"""
|
||||
self.write({'text': sample_text})
|
||||
|
||||
# 辅助方法
|
||||
def _generate_random_string(self, length):
|
||||
"""生成随机字符串"""
|
||||
letters = string.ascii_letters + string.digits
|
||||
return ''.join(random.choice(letters) for _ in range(length))
|
||||
|
||||
def _generate_random_date(self):
|
||||
"""生成随机日期(最近365天内)"""
|
||||
days_ago = random.randint(0, 365)
|
||||
return date.today() - timedelta(days=days_ago)
|
||||
|
||||
def _generate_random_datetime(self):
|
||||
"""生成随机日期时间(最近30天内)"""
|
||||
hours_ago = random.randint(0, 720) # 30天*24小时
|
||||
return datetime.now() - timedelta(hours=hours_ago)
|
||||
|
||||
# 生成随机数据的方法
|
||||
def action_insert_random_data(self):
|
||||
"""生成50条随机测试数据"""
|
||||
# 预先生成一些附件供选择
|
||||
attachment_ids = self.env['ir.attachment'].search([], limit=10)
|
||||
|
||||
for i in range(50):
|
||||
# 生成随机数据
|
||||
random_name = f"测试项目_{i + 1}_{self._generate_random_string(6)}"
|
||||
random_int = random.randint(1, 1000)
|
||||
random_float = round(random.uniform(1.0, 10000.0), 2)
|
||||
random_date = self._generate_random_date()
|
||||
random_datetime = self._generate_random_datetime()
|
||||
random_state = random.choice(['draft', 'done'])
|
||||
random_selection = random.choice(['1', '2'])
|
||||
random_bool = random.choice([True, False])
|
||||
|
||||
# 生成随机文本
|
||||
random_text = f"""
|
||||
这是第{i + 1}条测试记录的长文本内容。
|
||||
生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
随机ID:{self._generate_random_string(10)}
|
||||
描述:这是一条自动生成的测试数据,用于演示各种字段类型的显示效果。
|
||||
"""
|
||||
|
||||
# 创建主记录
|
||||
test_record = self.create({
|
||||
'name': random_name,
|
||||
'int_field': random_int,
|
||||
'float_field': random_float,
|
||||
'date_field': random_date,
|
||||
'datetime_field': random_datetime,
|
||||
'state': random_state,
|
||||
'bool_test': random_bool,
|
||||
'selection_test': random_selection,
|
||||
'text': random_text,
|
||||
})
|
||||
|
||||
# 随机选择附件
|
||||
if attachment_ids:
|
||||
# 随机选择1-3个附件
|
||||
num_attachments = random.randint(1, min(3, len(attachment_ids)))
|
||||
random_attachments = random.sample(attachment_ids.ids, num_attachments)
|
||||
test_record.write({
|
||||
'file_id': random.choice(attachment_ids).id if attachment_ids else False,
|
||||
'file_ids': [(6, 0, random_attachments)]
|
||||
})
|
||||
|
||||
# 返回成功消息
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': '数据生成成功',
|
||||
'message': f'已成功生成50条测试数据!',
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
'next': {'type': 'ir.actions.act_window_close'},
|
||||
}
|
||||
}
|
||||
|
||||
def action_to_dialog(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'model.test',
|
||||
'view_mode': 'form',
|
||||
'res_id': self.id,
|
||||
'views': [[self.env.ref('model_test.view_file_preview_test_form').id, 'form']],
|
||||
}
|
||||
|
||||
def cancel(self):
|
||||
self.state = 'done'
|
||||
|
||||
def test_approval(self):
|
||||
self.verify_rules(self.cancel)
|
||||
# print(asd)
|
||||
|
||||
|
||||
# class TimeCalculation(models.Model):
|
||||
# _name = 'example.time.calculation'
|
||||
# _description = 'Example for Start Time, End Time, Duration'
|
||||
#
|
||||
# start_time = fields.Datetime(string='Start Time', required=True)
|
||||
# end_time = fields.Datetime(string='End Time', required=True)
|
||||
# duration = fields.Float(string='Duration (hours)', compute='_compute_duration', inverse='_inverse_duration', store=True)
|
||||
#
|
||||
# @api.depends('start_time', 'end_time')
|
||||
# def _compute_duration(self):
|
||||
# """根据开始时间和结束时间计算时间间隔"""
|
||||
# for record in self:
|
||||
# if record.start_time and record.end_time:
|
||||
# delta = record.end_time - record.start_time
|
||||
# record.duration = delta.total_seconds() / 3600 # 转换为小时
|
||||
#
|
||||
# @api.depends('start_time', 'duration')
|
||||
# def _compute_end_time(self):
|
||||
# """根据开始时间和时间间隔计算结束时间"""
|
||||
# for record in self:
|
||||
# if record.start_time and record.duration is not None:
|
||||
# record.end_time = record.start_time + timedelta(hours=record.duration)
|
||||
#
|
||||
# @api.depends('end_time', 'duration')
|
||||
# def _compute_start_time(self):
|
||||
# """根据结束时间和时间间隔计算开始时间"""
|
||||
# for record in self:
|
||||
# if record.end_time and record.duration is not None:
|
||||
# record.start_time = record.end_time - timedelta(hours=record.duration)
|
||||
#
|
||||
# def _inverse_duration(self):
|
||||
# """反向更新结束时间"""
|
||||
# for record in self:
|
||||
# if record.start_time and record.duration is not None:
|
||||
# record.end_time = record.start_time + timedelta(hours=record.duration)
|
||||
#
|
||||
# @api.onchange('start_time', 'end_time')
|
||||
# def _onchange_duration(self):
|
||||
# """确保在界面交互中,时间关系自动更新"""
|
||||
# for record in self:
|
||||
# if record.start_time and record.end_time:
|
||||
# delta = record.end_time - record.start_time
|
||||
# record.duration = delta.total_seconds() / 3600
|
||||
#
|
||||
# @api.onchange('start_time', 'duration')
|
||||
# def _onchange_end_time(self):
|
||||
# """确保在界面交互中,时间关系自动更新"""
|
||||
# for record in self:
|
||||
# if record.start_time and record.duration is not None:
|
||||
# record.end_time = record.start_time + timedelta(hours=record.duration)
|
||||
#
|
||||
# @api.onchange('end_time', 'duration')
|
||||
# def _onchange_start_time(self):
|
||||
# """确保在界面交互中,时间关系自动更新"""
|
||||
# for record in self:
|
||||
# if record.end_time and record.duration is not None:
|
||||
# record.start_time = record.end_time - timedelta(hours=record.duration)
|
||||
@@ -1,3 +0,0 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_model_test,测试模块,model_model_test,,1,1,1,1
|
||||
access_model_test_many_one,测试模块多,model_model_test_many_one,,1,1,1,1
|
||||
|
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="ModelTest.buttons" t-inherit="web.ListView.Buttons" owl="1">
|
||||
<xpath expr="(//div/*)[last()]" position="after">
|
||||
<button type="button" class="btn btn-primary"
|
||||
t-on-click="(ev) => this.triggerDynamicColumnsChanged('btn_1', ['name','int_field','float_field'])">
|
||||
方案1
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary"
|
||||
t-on-click="(ev) => this.triggerDynamicColumnsChanged('btn_2', ['selection_test','text','one_to_many_field'])">
|
||||
方案2
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,10 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
import { registry } from '@web/core/registry';
|
||||
import { listView } from '@web/views/list/list_view';
|
||||
|
||||
|
||||
export const ModelTestListView = {
|
||||
...listView,
|
||||
buttonTemplate: 'ModelTest.buttons'
|
||||
};
|
||||
registry.category('views').add('model_test_list', ModelTestListView);
|
||||
@@ -1,126 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- 项目总览搜索 -->
|
||||
<record id="view_model_test_search_view" model="ir.ui.view">
|
||||
<field name="model">model.test</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="项目总览">
|
||||
<separator/>
|
||||
<filter string="真" name="filter_by_true" domain="[('bool_test', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="假" name="filter_by_false" domain="[('bool_test', '=', False)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="多对一未见" name="group_by_file_id" domain="[]" context="{'group_by': 'file_id'}" />
|
||||
<filter string="布尔值" name="group_by_bool_test" domain="[]" context="{'group_by': 'bool_test'}" />
|
||||
</group>
|
||||
<searchpanel>
|
||||
<field name="file_id"/>
|
||||
</searchpanel>
|
||||
<!--
|
||||
<header_search class="header-search-inline"> 所有搜索在一行
|
||||
-->
|
||||
<header_search>
|
||||
<field name="name"/>
|
||||
<field name="file_id"/>
|
||||
<field name="selection_test" options="{'radio': True}"/>
|
||||
<field name="bool_test" options="{'radio': True, 'no_label': True}"/>
|
||||
<select_date_filter allow_date_fields="create_date,write_date" />
|
||||
<quick_filter string="快捷筛选一" options="{'radio': True, 'no_label': True}">
|
||||
<filter string="真" name="filter_by_bool_true" domain="[('bool_test', '=', True)]"/>
|
||||
<filter string="最近的二十" name="filter_by_bool_true"/>
|
||||
</quick_filter>
|
||||
<field name="date_field" options="{'radio': True, 'no_label': True}"/>
|
||||
<field name="datetime_field"/>
|
||||
<quick_filter string="快捷筛选二" options="{'radio': True, 'no_label': True}">
|
||||
<filter string="假" name="filter_by_bool_false" domain="[('bool_test', '=', False)]"/>
|
||||
</quick_filter>
|
||||
</header_search>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- tree视图 -->
|
||||
<record id="view_model_test_tree" model="ir.ui.view">
|
||||
<field name="name">model.test.tree</field>
|
||||
<field name="model">model.test</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="">
|
||||
<header>
|
||||
<button name="action_insert_random_data" string="随机插入50条数据" type="object" class="btn-primary ms-1" display="always"/>
|
||||
</header>
|
||||
<field name="name"/>
|
||||
<field name="int_field"/>
|
||||
<field name="float_field"/>
|
||||
<field name="selection_test"/>
|
||||
<field name="text" widget="text_nowrap"/>
|
||||
<field name="one_to_many_field"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- form视图 -->
|
||||
<record id="view_model_test_form" model="ir.ui.view">
|
||||
<field name="name">model.test.form</field>
|
||||
<field name="model">model.test</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Job">
|
||||
<header>
|
||||
<button string="测试审批" type="object" name="test_approval" class="btn-primary"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="int_field"/>
|
||||
<field name="float_field"/>
|
||||
<field name="date_field"/>
|
||||
<field name="datetime_field"/>
|
||||
<field name="state"/>
|
||||
<field name="text"/>
|
||||
<field name="file_id"/>
|
||||
<field name="selection_test"/>
|
||||
<field name="bool_test" widget="boolean_toggle"/>
|
||||
<field name="file_ids"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="多对多字段图片预览测试">
|
||||
<field name="file_ids"/>
|
||||
</page>
|
||||
<page string="one2many字段">
|
||||
<field name="one_to_many_field" mode="tree">
|
||||
<tree editable="1">
|
||||
<field name="name"/>
|
||||
<field name="file_id"/>
|
||||
<field name="file_ids"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<!-- Attachment preview -->
|
||||
<div class="o_attachment_preview" groups="hr_recruitment.group_applicant_cv_display"/>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 跳转菜单 -->
|
||||
<record model="ir.actions.act_window" id="file_preview_menu_action_window">
|
||||
<field name="name">文件测试</field>
|
||||
<field name="target">main</field>
|
||||
<field name="res_model">model.test</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- 二级菜单 -->
|
||||
<menuitem
|
||||
id="model_test_menu"
|
||||
name="测试模块"
|
||||
sequence="10"
|
||||
action="file_preview_menu_action_window"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user