mirror of
https://github.com/guohuadeng/app-odoo.git
synced 2025-02-23 04:11:36 +02:00
Merge branch '16.0' of github.com:guohuadeng/app-odoo into 16.0
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<!-- 时间格式 -->
|
||||
<record id="base.lang_zh_CN" model="res.lang">
|
||||
<record id="base.lang_zh_CN" model="res.lang" context="{'lang': 'zh_CN'}">
|
||||
<field name="name">中文</field>
|
||||
<field name="date_format">%Y-%m-%d</field>
|
||||
<field name="time_format">%H:%M:%S</field>
|
||||
</record>
|
||||
<record id="base.lang_en" model="res.lang">
|
||||
<record id="base.lang_en" model="res.lang" context="{'lang': 'zh_CN'}">
|
||||
<field name="name">English</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="base.public_partner" model="res.partner">
|
||||
<record id="base.public_partner" model="res.partner" context="{'lang': 'zh_CN'}">
|
||||
<field name="name">公共访客用户</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="sales_team.team_sales_department" model="crm.team">
|
||||
<record id="sales_team.team_sales_department" model="crm.team" context="{'lang': 'zh_CN'}">
|
||||
<field name="name">中国</field>
|
||||
</record>
|
||||
<record id="sales_team.team_sales_department" model="crm.team" context="{'lang': 'en'}">
|
||||
<field name="name">China</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
{
|
||||
'name': 'ChatGPT4,Google Bard, AiGC Center.Ai服务中心,聚合全网Ai',
|
||||
'version': '16.23.07.29',
|
||||
'version': '16.23.08.15',
|
||||
'author': 'odooai.cn',
|
||||
'company': 'odooai.cn',
|
||||
'maintainer': 'odooai.cn',
|
||||
@@ -61,6 +61,7 @@
|
||||
'views/ai_robot_views.xml',
|
||||
'views/res_partner_ai_use_views.xml',
|
||||
'views/res_users_views.xml',
|
||||
'views/mail_channel_views.xml',
|
||||
],
|
||||
'assets': {
|
||||
'mail.assets_messaging': [
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import res_partner
|
||||
from . import mail_channel
|
||||
from . import res_config_settings
|
||||
from . import ai_robot
|
||||
from . import res_partner
|
||||
from . import res_partner_ai_use
|
||||
from . import res_users
|
||||
from . import mail_message
|
||||
|
||||
@@ -15,8 +15,84 @@ _logger = logging.getLogger(__name__)
|
||||
|
||||
class Channel(models.Model):
|
||||
_inherit = 'mail.channel'
|
||||
|
||||
def get_openai_context(self, channel_id, author_id, answer_id, minutes=30, chat_count=1):
|
||||
|
||||
is_private = fields.Boolean(string="私有频道", default=False, help="私人频道不公开,可邀请及清退指定用户")
|
||||
# 因为 channel_member_ids 不好处理,在此增加此字段
|
||||
# 主Ai
|
||||
ai_partner_id = fields.Many2one(comodel_name="res.partner", string="专属主Ai", required=False,
|
||||
domain=[('gpt_id', '!=', None), ('is_chat_private', '=', True)],
|
||||
default=lambda self: self._app_get_m2o_default('ai_partner_id'),
|
||||
help="主Ai是主要对话对象,当没有@操作时,由主Ai回答", )
|
||||
ext_ai_partner_id = fields.Many2one(comodel_name="res.partner", string="辅助Ai",
|
||||
domain=[('gpt_id', '!=', None), ('is_chat_private', '=', True)],
|
||||
help="通过 @辅助Ai 可以让辅助Ai回答问题", )
|
||||
description = fields.Char('Ai角色设定', help="填写后,Ai将以您设定的身份与你交互,如:你是一个在航空航天领域的专家。不填则根据问题智能处理")
|
||||
set_max_tokens = fields.Selection([
|
||||
('300', '简短'),
|
||||
('600', '标准'),
|
||||
('1000', '中等'),
|
||||
('2000', '长篇'),
|
||||
('3000', '超长篇'),
|
||||
('32000', '32K'),
|
||||
], string='响应篇幅限制', default='600', help="越大返回内容越多,计费也越多")
|
||||
set_chat_count = fields.Selection([
|
||||
('none', 'Ai自动判断'),
|
||||
('1', '1标准'),
|
||||
('3', '3强关联'),
|
||||
('5', '5超强关联'),
|
||||
], string="上下文相关", default='1', help="0-5,设定后,会将最近n次对话发给Ai,有助于他更好的回答,但太大费用也高")
|
||||
set_temperature = fields.Selection([
|
||||
('2', '天马行空'),
|
||||
('1.5', '创造性'),
|
||||
('1', '标准'),
|
||||
('0.6', '理性'),
|
||||
('0.1', '保守'),
|
||||
], string="创造性", default='1', help="0-21,值越大越富有想像力,越小则越保守")
|
||||
set_top_p = fields.Selection([
|
||||
('0.9', '严谨惯性思维'),
|
||||
('0.6', '标准推理'),
|
||||
('0.4', '跳跃性'),
|
||||
('0.1', '随便'),
|
||||
], string="思维连贯性", default='0.6', help="0-1,值越大越倾向大众化的连贯思维")
|
||||
# 避免使用常用词
|
||||
set_frequency_penalty = fields.Selection([
|
||||
('2', '老学究-晦涩难懂'),
|
||||
('1.5', '学院派-较多高级词'),
|
||||
('1', '标准'),
|
||||
('0.1', '少常用词'),
|
||||
('-1', '通俗易懂'),
|
||||
('-2', '大白话'),
|
||||
], string='语言风格', default='1', help="-2~2,值越大越少使用常用词")
|
||||
set_presence_penalty = fields.Selection([
|
||||
('2', '多样强迫症'),
|
||||
('1.5', '新颖化'),
|
||||
('1', '标准'),
|
||||
('0.1', '允许常规重复'),
|
||||
('-1', '允许较多重复'),
|
||||
('-2', '更多强调重复'),
|
||||
], string='用词多样性', default='1', help="-2~2,值越大越少重复词")
|
||||
|
||||
# todo: 这里用 compute?
|
||||
max_tokens = fields.Integer('最长响应Token', default=600, help="越大返回内容越多,计费也越多")
|
||||
chat_count = fields.Integer(string="上下文数量", default=0, help="0~3,设定后,会将最近n次对话发给Ai,有助于他更好的回答")
|
||||
temperature = fields.Float(string="创造性值", default=1, help="0~2,值越大越富有想像力,越小则越保守")
|
||||
top_p = fields.Float(string="连贯性值", default=0.6, help="0~1,值越大越富有想像力,越小则越保守")
|
||||
frequency_penalty = fields.Float('避免常用词值', default=1, help="-2~2,值越大越少使用常用词")
|
||||
presence_penalty = fields.Float('避免重复词值', default=1, help="-2~2,值越大越少重复词")
|
||||
|
||||
is_current_channel = fields.Boolean('是否当前用户默认频道', compute='_compute_is_current_channel', help='是否当前用户默认微信对话频道')
|
||||
|
||||
def name_get(self):
|
||||
result = []
|
||||
for c in self:
|
||||
if c.channel_type == 'channel' and c.is_private:
|
||||
pre = '[私]'
|
||||
else:
|
||||
pre = ''
|
||||
result.append((c.id, "%s%s" % (pre, c.name or '')))
|
||||
return result
|
||||
|
||||
def get_openai_context(self, channel_id, author_id, answer_id, minutes=30, chat_count=0):
|
||||
# 上下文处理,要处理群的方式,以及独聊的方式
|
||||
# azure新api 处理
|
||||
context_history = []
|
||||
@@ -39,7 +115,10 @@ class Channel(models.Model):
|
||||
domain = expression.AND([domain, [('date', '>=', afterTime)]])
|
||||
else:
|
||||
domain = expression.AND([domain, [('author_id', '=', answer_id.id)]])
|
||||
ai_msg_list = message_model.with_context(tz='UTC').search(domain, order="id desc", limit=chat_count)
|
||||
if chat_count == 0:
|
||||
ai_msg_list = []
|
||||
else:
|
||||
ai_msg_list = message_model.with_context(tz='UTC').search(domain, order="id desc", limit=chat_count)
|
||||
for ai_msg in ai_msg_list:
|
||||
# 判断这个 ai_msg 是不是ai发,有才 insert。 判断 user_msg 是不是 user发的,有才 insert
|
||||
user_msg = ai_msg.parent_id.sudo()
|
||||
@@ -95,7 +174,9 @@ class Channel(models.Model):
|
||||
|
||||
# 不处理 一般notify,但处理欢迎
|
||||
if '<div class="o_mail_notification' in message.body and message.body != _('<div class="o_mail_notification">joined the channel</div>'):
|
||||
return rdata
|
||||
return rdata
|
||||
if 'o_odoobot_command' in message.body:
|
||||
return rdata
|
||||
|
||||
if channel_type == 'chat':
|
||||
channel_partner_ids = self.channel_partner_ids
|
||||
@@ -155,7 +236,8 @@ class Channel(models.Model):
|
||||
# elif user_id.gpt_id and not is_allow:
|
||||
# # 暂时有限用户的Ai
|
||||
# raise UserError(_('此Ai暂时未开放,请联系管理员。'))
|
||||
|
||||
if hasattr(ai, 'is_translator') and ai.is_translator:
|
||||
return rdata
|
||||
chatgpt_channel_id = self.env.ref('app_chatgpt.channel_chatgpt')
|
||||
|
||||
if message.body == _('<div class="o_mail_notification">joined the channel</div>'):
|
||||
@@ -186,12 +268,14 @@ class Channel(models.Model):
|
||||
openai.api_key = api_key
|
||||
# 非4版本,取0次。其它取3 次历史
|
||||
chat_count = 3
|
||||
if '4' in ai.ai_model:
|
||||
if '4' in ai.ai_model or '4' in ai.name:
|
||||
chat_count = 1
|
||||
if hasattr(self, 'chat_count'):
|
||||
if self.chat_count > 0:
|
||||
chat_count = 1
|
||||
else:
|
||||
chat_count = chat_count
|
||||
|
||||
if author_id != answer_id.id and self.channel_type == 'chat':
|
||||
# 私聊
|
||||
_logger.info(f'私聊:author_id:{author_id},partner_chatgpt.id:{answer_id.id}')
|
||||
|
||||
@@ -1,9 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import fields, models, api
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
gpt_id = fields.Many2one('ai.robot', string='Bind to ChatGpt')
|
||||
|
||||
is_chat_private = fields.Boolean('Allow Chat Private', default=False)
|
||||
|
||||
@api.model
|
||||
def im_search(self, name, limit=20):
|
||||
users = self.env['res.users'].search([
|
||||
('id', '!=', self.env.user.id),
|
||||
('name', 'ilike', name),
|
||||
('active', '=', True),
|
||||
('share', '=', False),
|
||||
('is_chat_private', '=', True)
|
||||
], order='gpt_id, name, id', limit=limit)
|
||||
return list(users.partner_id.mail_partner_format().values())
|
||||
|
||||
def mail_partner_format(self, fields=None):
|
||||
# 直接覆盖原生,增加 gpt_id 字段
|
||||
partners_format = dict()
|
||||
if not fields:
|
||||
fields = {'id': True, 'name': True, 'email': True, 'active': True, 'im_status': True, 'gpt_id': 0, 'user': {}}
|
||||
for partner in self:
|
||||
data = {}
|
||||
if 'id' in fields:
|
||||
data['id'] = partner.id
|
||||
if 'name' in fields:
|
||||
name = partner.name
|
||||
# 英文不好分,暂时不隐名
|
||||
# if not partner.related_user_id.gpt_id:
|
||||
# name = partner.name[0] + '*' * (len(partner.name) - 1)
|
||||
data['name'] = name
|
||||
if 'email' in fields:
|
||||
data['email'] = partner.email
|
||||
if 'active' in fields:
|
||||
data['active'] = partner.active
|
||||
if 'im_status' in fields:
|
||||
data['im_status'] = partner.im_status
|
||||
if 'gpt_id' in fields:
|
||||
data['gpt_id'] = partner.gpt_id.id if partner.gpt_id else 0
|
||||
if 'user' in fields:
|
||||
internal_users = partner.user_ids - partner.user_ids.filtered('share')
|
||||
main_user = internal_users[0] if len(internal_users) > 0 else partner.user_ids[0] if len(partner.user_ids) > 0 else self.env['res.users']
|
||||
data['user'] = {
|
||||
"id": main_user.id,
|
||||
"isInternalUser": not main_user.share,
|
||||
} if main_user else [('clear',)]
|
||||
# if 'guest' in self.env.context or not self.env.user.has_group('base.group_erp_manager'):
|
||||
# 完全不显示 邮箱
|
||||
data.pop('email', None)
|
||||
partners_format[partner] = data
|
||||
return partners_format
|
||||
|
||||
@@ -14,3 +14,4 @@ class ResUsers(models.Model):
|
||||
], string='Allowed Conversation Mode', default='all', ondelete='set default')
|
||||
gpt_wl_partners = fields.Many2many('res.partner', 'res_partner_ai_use', 'ai_user_id', 'name', string='Allowed Partners')
|
||||
gpt_demo_time = fields.Integer('Default Demo Time', default=0)
|
||||
is_chat_private = fields.Boolean('Allow Chat Private', default=False, related='partner_id.is_chat_private', inherited=True, readonly=False)
|
||||
@@ -1,4 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_gpt_robt_manager,AiRobotUser,model_ai_robot,base.group_erp_manager,1,1,1,1
|
||||
access_res_partner_ai_use_user,ResPartnerAiUseUser,model_res_partner_ai_use,base.group_user,1,0,0,0
|
||||
access_res_partner_ai_use_manager,ResPartnerAiUseUser,model_res_partner_ai_use,sales_team.group_sale_manager,1,1,1,1
|
||||
access_res_partner_ai_use_manager,ResPartnerAiUseUser,model_res_partner_ai_use,base.group_erp_manager,1,1,1,1
|
||||
|
@@ -11,6 +11,6 @@
|
||||
<field name="name">All AI Use</field>
|
||||
<field ref="model_res_partner_ai_use" name="model_id"/>
|
||||
<field name="domain_force">[(1,'=',1)]</field>
|
||||
<field name="groups" eval="[(4, ref('sales_team.group_sale_manager'))]"/>
|
||||
<field name="groups" eval="[(4, ref('base.group_erp_manager'))]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
111
app_chatgpt/views/mail_channel_views.xml
Normal file
111
app_chatgpt/views/mail_channel_views.xml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- form,原生继承以便管理-->
|
||||
<record id="ai_mail_channel_view_form" model="ir.ui.view">
|
||||
<field name="name">ai.mail.channel.form</field>
|
||||
<field name="model">mail.channel</field>
|
||||
<field name="mode">extension</field>
|
||||
<field name="inherit_id" ref="mail.mail_channel_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='privacy']" position="before">
|
||||
<page name="page_user" string="用户设定的角色相关,一般不要调整">
|
||||
<group>
|
||||
<group name="role_set" string="Ai常规设定">
|
||||
<field name="channel_type" readonly="1"/>
|
||||
<field name="ai_partner_id"
|
||||
options="{'no_open': True, 'no_create': True}"/>
|
||||
<field name="ext_ai_partner_id"
|
||||
options="{'no_open': True, 'no_create': True}"/>
|
||||
</group>
|
||||
<group name="param_set" string="Ai角色风格设定">
|
||||
<div class="o_td_label">
|
||||
<label for="set_max_tokens"/>
|
||||
</div>
|
||||
<field name="set_max_tokens" nolabel="1" required="1"/>
|
||||
<div class="o_td_label">
|
||||
<label for="set_chat_count"/>
|
||||
</div>
|
||||
<field name="set_chat_count" nolabel="1" required="1"/>
|
||||
<div class="o_td_label">
|
||||
<label for="set_temperature"/>
|
||||
</div>
|
||||
<field name="set_temperature" nolabel="1" required="1"/>
|
||||
<div class="o_td_label">
|
||||
<label for="set_top_p"/>
|
||||
</div>
|
||||
<field name="set_top_p" nolabel="1" required="1"/>
|
||||
<div class="o_td_label">
|
||||
<label for="set_frequency_penalty"/>
|
||||
</div>
|
||||
<field name="set_frequency_penalty" nolabel="1" required="1"/>
|
||||
<div class="o_td_label">
|
||||
<label for="set_presence_penalty"/>
|
||||
</div>
|
||||
<field name="set_presence_penalty" nolabel="1" required="1"/>
|
||||
<field name="is_private" readonly="0"/>
|
||||
<field name="create_uid" readonly="1" options="{'no_open': True, 'no_create': True}"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='group_public_id']/.." position="after">
|
||||
<group string="Ai智能优化设定,具体参数">
|
||||
<field name="chat_count"/>
|
||||
<p class="ml16 my-n1 mb16" colspan="2">0-3,设定后,会将最近n次对话发给Ai,有助于他更好的回答</p>
|
||||
<field name="max_tokens"/>
|
||||
<p class="ml16 my-n1 mb16" colspan="2">最大响应Token,控制返回内容长度</p>
|
||||
<field name="temperature"/>
|
||||
<p class="ml16 my-n1 mb16" colspan="2">0-1,值越大越富有想像力,越小则越保守</p>
|
||||
<field name="frequency_penalty"/>
|
||||
<p class="ml16 my-n1 mb16" colspan="2">0-1,值越大越少使用常用词</p>
|
||||
<field name="presence_penalty"/>
|
||||
<p class="ml16 my-n1 mb16" colspan="2">0-1,值越大越少重复词</p>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--kanban,原生的处理-->
|
||||
<record id="ai_mail_channel_view_kanban" model="ir.ui.view">
|
||||
<field name="model">mail.channel</field>
|
||||
<field name="inherit_id" ref="mail.mail_channel_view_kanban"/>
|
||||
<field name="mode">extension</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//kanban//field[1]" position="before">
|
||||
<field name="is_private"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='description']" position="before">
|
||||
<em>角色:</em>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='channel_join']" position="replace">
|
||||
<button attrs="{'invisible':[('is_private','=',True), ('group_ids', '!=', [])]}"
|
||||
class="btn btn-warning float-end" type="edit">
|
||||
智能设定
|
||||
</button>
|
||||
<button type="object" attrs="{'invisible':['|', ('is_member','=',True), ('group_ids', '!=', [])]}" class="btn btn-primary float-end" name="channel_join">进入频道</button>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_unfollow']" position="replace">
|
||||
<button type="object" attrs="{'invisible':['|', ('is_member','=',False), ('group_ids', '!=', [])]}"
|
||||
class="btn btn-secondary float-end" name="action_unfollow">暂时离开</button>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!--search 原生处理-->
|
||||
<record id="ai_mail_channel_view_search" model="ir.ui.view">
|
||||
<field name="model">mail.channel</field>
|
||||
<field name="inherit_id" ref="mail.mail_channel_view_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="channel_type"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Channel Type" name="groupby_channel_type" domain="[]" context="{'group_by': 'channel_type'}"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
{
|
||||
'name': "odooai Odooapp Common Func",
|
||||
'version': '16.23.05.09',
|
||||
'version': '16.23.08.17',
|
||||
'author': 'odooai.cn',
|
||||
'category': 'Base',
|
||||
'website': 'https://www.odooai.cn',
|
||||
|
||||
@@ -29,6 +29,7 @@ from . import res_users
|
||||
from . import ir_mail_server
|
||||
from . import mail_mail
|
||||
from . import ir_http
|
||||
from . import app_import
|
||||
|
||||
|
||||
|
||||
|
||||
54
app_common/models/app_import.py
Normal file
54
app_common/models/app_import.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import base64
|
||||
import io
|
||||
import csv
|
||||
import os.path
|
||||
|
||||
from odoo import api, fields, models, modules, tools, SUPERUSER_ID, _
|
||||
from odoo.tools import pycompat
|
||||
from odoo.tests import common
|
||||
ADMIN_USER_ID = common.ADMIN_USER_ID
|
||||
|
||||
def app_quick_import(cr, content_path, sep=None):
|
||||
if not sep:
|
||||
sep = '/'
|
||||
dir_split = content_path.split(sep)
|
||||
module_name = dir_split[0]
|
||||
file_name = dir_split[2]
|
||||
file_path, file_type = os.path.splitext(content_path)
|
||||
model_name = file_name.replace(file_type, '')
|
||||
file_path = modules.get_module_resource(module_name, dir_split[1], file_name)
|
||||
content = open(file_path, 'rb').read()
|
||||
uid = SUPERUSER_ID
|
||||
if model_name == 'mail.channel':
|
||||
# todo: 创建mail.channel时,如果用root用户会报错
|
||||
uid = 2
|
||||
env = api.Environment(cr, uid, {})
|
||||
if file_type == '.csv':
|
||||
file_type = 'text/csv'
|
||||
elif file_type in ['.xls', '.xlsx']:
|
||||
file_type = 'application/vnd.ms-excel'
|
||||
import_wizard = env['base_import.import'].create({
|
||||
'res_model': model_name,
|
||||
'file_name': file_name,
|
||||
'file_type': file_type,
|
||||
'file': content,
|
||||
})
|
||||
if file_type == 'text/csv':
|
||||
preview = import_wizard.parse_preview({
|
||||
'separator': ',',
|
||||
'has_headers': True,
|
||||
'quoting': '"',
|
||||
})
|
||||
elif file_type == 'application/vnd.ms-excel':
|
||||
preview = import_wizard.parse_preview({
|
||||
'has_headers': True,
|
||||
})
|
||||
result = import_wizard.execute_import(
|
||||
preview["headers"],
|
||||
preview["headers"],
|
||||
preview["options"]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import wizard
|
||||
from . import hooks
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
{
|
||||
'name': 'odoo 16 Customize OEM(Boost, Data reset)',
|
||||
'version': '16.23.08.05',
|
||||
'version': '16.23.08.15',
|
||||
'author': 'odooai.cn',
|
||||
'category': 'Extra Tools',
|
||||
'website': 'https://www.odooai.cn',
|
||||
@@ -135,6 +135,9 @@
|
||||
40. Fix support for enterprise version.
|
||||
41. Fix odoo bug, when click Preferences menu not hide in mobile.
|
||||
42. Add menu navbar setup for top or bottom. navigator footer support.
|
||||
43. Check to only Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.
|
||||
44. Check to stop subscribe and follow. This to make odoo speed up.
|
||||
45. Add addons path info to module.
|
||||
|
||||
This module can help to white label the Odoo.
|
||||
Also helpful for training and support for your odoo end-user.
|
||||
@@ -183,5 +186,9 @@
|
||||
39. 只有系统管理员可以操作快速debug
|
||||
40. 增强对企业版的支持
|
||||
41. 修正odoo原生移动端菜单bug,点击个人设置时,原菜单不隐藏等
|
||||
42. 可设置导航栏在上方还是下方,分开桌面与移动端.
|
||||
43. 可设置只允许管理员进入开发者模式,不可在url中直接debut=1来调试
|
||||
44. 可配置停用自动用户订阅功能,这会提速odoo,减少资源消耗
|
||||
45. 为应用模块增加模块路径信息
|
||||
""",
|
||||
}
|
||||
|
||||
4
app_odoo_customize/controllers/__init__.py
Normal file
4
app_odoo_customize/controllers/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# todo: website 有bug oauth
|
||||
from . import controllers
|
||||
25
app_odoo_customize/controllers/controllers.py
Normal file
25
app_odoo_customize/controllers/controllers.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import http
|
||||
from odoo.addons.portal.controllers.web import Home
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class KsHome(Home):
|
||||
|
||||
@http.route()
|
||||
def web_client(self, s_action=None, **kw):
|
||||
res = super(KsHome, self).web_client(s_action, **kw)
|
||||
|
||||
if kw.get('debug') in ['1', 'assets', 'assets,tests']:
|
||||
config_parameter = request.env['ir.config_parameter'].sudo()
|
||||
app_debug_only_admin = config_parameter.get_param('app_debug_only_admin')
|
||||
if request.session.uid and request.env.user.browse(request.session.uid)._is_admin():
|
||||
pass
|
||||
else:
|
||||
if app_debug_only_admin:
|
||||
return request.redirect('/web/session/logout?debug=0')
|
||||
return res
|
||||
|
||||
|
||||
|
||||
@@ -22,5 +22,6 @@
|
||||
<function model="ir.config_parameter" name="set_param" eval="('app_ribbon_background_color', 'rgba(255,0,0,.4)')"/>
|
||||
<function model="ir.config_parameter" name="set_param" eval="('app_navbar_pos_pc', 'top')"/>
|
||||
<function model="ir.config_parameter" name="set_param" eval="('app_navbar_pos_mobile', 'bottom')"/>
|
||||
<function model="ir.config_parameter" name="set_param" eval="('app_debug_only_admin', 'True')"/>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -6,5 +6,6 @@ from . import ir_module_module
|
||||
from . import web_environment_ribbon_backend
|
||||
from . import ir_http
|
||||
from . import ir_module_addons_path
|
||||
from . import mail_thread
|
||||
# from . import ir_ui_view
|
||||
# from . import ir_ui_menu
|
||||
|
||||
@@ -32,4 +32,7 @@ class IrHttp(models.AbstractModel):
|
||||
# 增加 bar位置处理
|
||||
result['app_navbar_pos_pc'] = config_parameter.get_param('app_navbar_pos_pc', 'top')
|
||||
result['app_navbar_pos_mobile'] = config_parameter.get_param('app_navbar_pos_mobile', 'top')
|
||||
# 此处直接取,不用 session
|
||||
result['app_debug_only_admin'] = config_parameter.get_param('app_debug_only_admin')
|
||||
result['app_stop_subscribe'] = config_parameter.get_param('app_stop_subscribe')
|
||||
return result
|
||||
|
||||
@@ -18,6 +18,7 @@ class IrModule(models.Model):
|
||||
local_updatable = fields.Boolean('Local updatable', compute=False, default=False, store=True)
|
||||
addons_path_id = fields.Many2one('ir.module.addons.path', string='Addons Path ID', readonly=True)
|
||||
addons_path = fields.Char(string='Addons Path', related='addons_path_id.path', readonly=True)
|
||||
license = fields.Char(readonly=True)
|
||||
|
||||
def module_multi_uninstall(self):
|
||||
""" Perform the various steps required to uninstall a module completely
|
||||
|
||||
44
app_odoo_customize/models/mail_thread.py
Normal file
44
app_odoo_customize/models/mail_thread.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
|
||||
|
||||
class MailThread(models.AbstractModel):
|
||||
_inherit = "mail.thread"
|
||||
|
||||
def message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None):
|
||||
""" 停用订阅功能. """
|
||||
ir_config = self.env['ir.config_parameter']
|
||||
app_stop_subscribe = True if ir_config.get_param('app_stop_subscribe', False) == "True" else False
|
||||
if app_stop_subscribe:
|
||||
return True
|
||||
else:
|
||||
return super(MailThread, self).message_subscribe(partner_ids, subtype_ids)
|
||||
|
||||
def _message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None, customer_ids=None):
|
||||
""" 停用订阅功能. """
|
||||
ir_config = self.env['ir.config_parameter']
|
||||
app_stop_subscribe = True if ir_config.get_param('app_stop_subscribe', False) == "True" else False
|
||||
if app_stop_subscribe:
|
||||
return True
|
||||
else:
|
||||
return super(MailThread, self)._message_subscribe(partner_ids, subtype_ids, customer_ids)
|
||||
|
||||
def _message_auto_subscribe_followers(self, updated_values, default_subtype_ids):
|
||||
""" 停用订阅功能. """
|
||||
ir_config = self.env['ir.config_parameter']
|
||||
app_stop_subscribe = True if ir_config.get_param('app_stop_subscribe', False) == "True" else False
|
||||
if app_stop_subscribe:
|
||||
return []
|
||||
else:
|
||||
return super(MailThread, self)._message_auto_subscribe_followers(updated_values, default_subtype_ids)
|
||||
|
||||
def _message_auto_subscribe_notify(self, partner_ids, template):
|
||||
""" 停用订阅功能. """
|
||||
ir_config = self.env['ir.config_parameter']
|
||||
app_stop_subscribe = True if ir_config.get_param('app_stop_subscribe', False) == "True" else False
|
||||
if app_stop_subscribe:
|
||||
return True
|
||||
else:
|
||||
return super(MailThread, self)._message_auto_subscribe_notify(partner_ids, template)
|
||||
@@ -54,6 +54,12 @@ class ResConfigSettings(models.TransientModel):
|
||||
('bottom', 'Bottom'),
|
||||
# ('left', 'Left'),
|
||||
], config_parameter='app_navbar_pos_mobile')
|
||||
|
||||
# 安全与提速
|
||||
app_debug_only_admin = fields.Boolean('Debug for Admin', config_parameter='app_debug_only_admin',
|
||||
help="Check to only Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.")
|
||||
app_stop_subscribe = fields.Boolean('Stop Odoo Subscribe', help="Check to stop subscribe and follow. This to make odoo speed up.",
|
||||
config_parameter='app_stop_subscribe')
|
||||
|
||||
def set_module_url(self):
|
||||
sql = "UPDATE ir_module_module SET website = '%s' WHERE license like '%s' and website <> ''" % (self.app_enterprise_url, 'OEEL%')
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 430 KiB After Width: | Height: | Size: 449 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 258 KiB |
@@ -47,7 +47,12 @@
|
||||
<div class="oe_span12">
|
||||
<h2 class="oe_slogan">This is a Long Term Support Apps.</h2>
|
||||
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||
<h3>Update: v16.23.05.24</h3>
|
||||
<h3>Update: v16.23.08.15</h3>
|
||||
<p>43. Check to only Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.</p>
|
||||
<p>44. Check to stop subscribe and follow. This to make odoo speed up.</p>
|
||||
<h3>Update: v16.23.07.25</h3>
|
||||
<p>42. Add menu navbar setup for top or bottom. navigator footer support.</p>
|
||||
<h3>Update: v16.23.07.14</h3>
|
||||
<p>41. Fix odoo bug, when click Preferences menu not hide in mobile.</p>
|
||||
<h3>Update: v16.23.05.04</h3>
|
||||
<p>Fix bug in mobile view in popup menu.</p>
|
||||
|
||||
@@ -29,6 +29,14 @@
|
||||
<field name="app_navbar_pos_mobile"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Security and Boost" name="app_security_boost">
|
||||
<group>
|
||||
<field name="app_debug_only_admin"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="app_stop_subscribe"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="User Menu">
|
||||
<group>
|
||||
<field name="app_show_lang"/>
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
<field name="model">ir.module.module</field>
|
||||
<field name="inherit_id" ref="base.view_module_filter" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<field name="author"/>
|
||||
</field>
|
||||
<filter name="not_installed" position="after">
|
||||
<filter name="is_local_updatable" string="Local updatable" domain="[('local_updatable', '=', True)]"/>
|
||||
</filter>
|
||||
@@ -33,25 +36,22 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="app_module_form" model="ir.ui.view">
|
||||
<field name="name">app.ir.module.module.form</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
<field name="inherit_id" ref="base.module_form" />
|
||||
<field name="arch" type="xml">
|
||||
<h3 class="oe_fade" position="attributes">
|
||||
<attribute name="groups">app_odoo_customize.group_show_author_in_apps</attribute>
|
||||
</h3>
|
||||
<field name="website" position="attributes">
|
||||
<attribute name="groups">app_odoo_customize.group_show_author_in_apps</attribute>
|
||||
</field>
|
||||
<xpath expr="//notebook/page/group" position="inside">
|
||||
<group name="module_path">
|
||||
<field name="addons_path_id" invisible="1"/>
|
||||
<field name="addons_path" groups="base.group_no_one"/>
|
||||
</group>
|
||||
</xpath>
|
||||
<field name="inherit_id" ref="base.module_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<h3 class="oe_fade" position="attributes">
|
||||
<attribute name="groups">app_odoo_customize.group_show_author_in_apps</attribute>
|
||||
</h3>
|
||||
<field name="category_id" position="after">
|
||||
<field name="addons_path_id" invisible="1"/>
|
||||
<field name="addons_path" groups="base.group_no_one"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="app_module_view_kanban" model="ir.ui.view">
|
||||
<field name="name">ir.module.module.kanban.inherit.base</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
<xpath expr="//search">
|
||||
<searchpanel view_types="list,kanban,pivot,graph,activity,search" class="app_test">
|
||||
<field name="categ_id" text="name" enable_counters="1" expand="1" limit="1000"/>
|
||||
<field name="type" enable_counters="1"/>
|
||||
<field name="product_tag_ids" select="multi" enable_counters="1"/>
|
||||
<field name="type"/>
|
||||
<field name="product_tag_ids" select="multi"/>
|
||||
</searchpanel>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//search">
|
||||
<searchpanel view_types="list,kanban,pivot,graph">
|
||||
<field name="status" enable_counters="1"/>
|
||||
<field name="state" enable_counters="1"/>
|
||||
<field name="invoice_status" enable_counters="1"/>
|
||||
<field name="partner_id" filter_domain="[('customer_rank','>', 0)]" limit="1000" enable_counters="1"/>
|
||||
<field name="team_id" enable_counters="1"/>
|
||||
|
||||
@@ -18,4 +18,13 @@
|
||||
Discard
|
||||
</xpath>
|
||||
</t>
|
||||
<!-- 创建移到左边-->
|
||||
<t t-name="app_web_enterprise.FormControlPanel" t-inherit="web.FormControlPanel" t-inherit-mode="extension">
|
||||
<xpath expr="//div[hasclass('o_cp_bottom_right')]//t[3]" position="replace"/>
|
||||
<!-- <xpath expr="//t[t-slot='control-panel-create-button']" position="replace"/>-->
|
||||
<!-- <xpath expr="//div[hasclass('o_cp_top_left')]" position="replace"/>-->
|
||||
<xpath expr="//div[hasclass('o_cp_top_left')]" position="inside">
|
||||
<t t-slot="control-panel-create-button" />
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<field name="model">blog.post</field>
|
||||
<field name="inherit_id" ref="website_blog.view_blog_post_list"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']" position="before">
|
||||
<field name="id" optional="show"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='website_url']" position="attributes">
|
||||
<attribute name="optional">hide</attribute>
|
||||
</xpath>
|
||||
@@ -14,6 +17,9 @@
|
||||
<xpath expr="//field[@name='is_published']" position="attributes">
|
||||
<attribute name="optional">show</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='website_url']" position="after">
|
||||
<field name="seo_name"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='author_id']" position="before">
|
||||
<field name="visits" optional="show"/>
|
||||
</xpath>
|
||||
@@ -32,7 +38,10 @@
|
||||
<field name="model">blog.post</field>
|
||||
<field name="inherit_id" ref="website_blog.view_blog_post_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='seo']" position="before">
|
||||
<xpath expr="//field[@name='tag_ids']" position="after">
|
||||
<field name="seo_name"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='seo']" position="after">
|
||||
<page name="post_content" string="Content">
|
||||
<field name="content" widget="html" nolabel="1" class="oe-bordered-editor"
|
||||
options="{'style-inline': true, 'codeview': true, 'dynamic_placeholder': true}"/>
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//search" position="inside">
|
||||
<searchpanel>
|
||||
<field name="blog_id" enable_counters="1"/>
|
||||
<field name="blog_id" enable_counters="1" select="multi"/>
|
||||
<field name="tag_ids" enable_counters="1" select="multi"/>
|
||||
</searchpanel>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Product -->
|
||||
<record id="app_product_template_search_view" model="ir.ui.view">
|
||||
<field name="name">app.product.template.search</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="app_product_superbar.app_product_template_search_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//searchpanel//field[@name='categ_id']" position="before">
|
||||
<field name="public_categ_ids" select="multi" text="name"/>
|
||||
<field name="website_ribbon_id" select="multi"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
<data>
|
||||
<!-- Product -->
|
||||
<record id="app_product_template_search_view" model="ir.ui.view">
|
||||
<field name="name">app.product.template.search</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="app_product_superbar.app_product_template_search_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//searchpanel//field[@name='categ_id']" position="before">
|
||||
<field name="public_categ_ids" enable_counters="1" select="multi" text="name"/>
|
||||
<field name="website_ribbon_id" enable_counters="1" select="multi"/>
|
||||
<field name="product_tag_ids" enable_counters="1" select="multi"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -25,7 +25,20 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='sales']" position="after">
|
||||
<page name="shop_desc" string="Website Description" attrs="{'invisible': [('sale_ok','=',False)]}">
|
||||
<field name="website_description" nolabel="1"/>
|
||||
<group>
|
||||
<group>
|
||||
<!-- 这里加个快速设置 seo url字段-->
|
||||
<field name="website_meta_title" invisible="1"/>
|
||||
<field name="website_meta_keywords"/>
|
||||
<field name="seo_name"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="website_meta_description"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Content">
|
||||
<field name="website_description" nolabel="1"/>
|
||||
</group>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user