int app-odoo 17, paid
7
app_common/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from .hooks import pre_init_hook
|
||||||
|
from .hooks import post_init_hook
|
||||||
|
from .hooks import uninstall_hook
|
||||||
90
app_common/__manifest__.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 2023-02-02
|
||||||
|
# author: 欧度智能,https://www.odooai.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of odooai
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||||
|
|
||||||
|
# Odoo16在线用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/16.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo16在线开发者手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/16.0/zh_CN/developer.html
|
||||||
|
|
||||||
|
# Odoo13在线用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/13.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo13在线开发者手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/13.0/index.html
|
||||||
|
|
||||||
|
# Odoo10在线中文用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.odooai.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.odooai.cn/odoo10_developer_document_offline/
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (C) 2009-TODAY odooai.cn Ltd. https://www.odooai.cn
|
||||||
|
# Author: Ivan Deng,300883@qq.com
|
||||||
|
# You can modify it under the terms of the GNU LESSER
|
||||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||||
|
# See <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies
|
||||||
|
# of the Software or modified copies of the Software.
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': "odooai Odooapp Common Func",
|
||||||
|
'version': '16.5.23.09.27',
|
||||||
|
'author': 'odooai.cn',
|
||||||
|
'category': 'Base',
|
||||||
|
'website': 'https://www.odooai.cn',
|
||||||
|
'live_test_url': 'https://demo.odooapp.cn',
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'sequence': 2,
|
||||||
|
'price': 0.00,
|
||||||
|
'currency': 'EUR',
|
||||||
|
'images': ['static/description/banner.png'],
|
||||||
|
'summary': '''
|
||||||
|
Core for common use odooai apps.
|
||||||
|
基础核心,必须没有要被依赖字段及视图等,实现auto_install
|
||||||
|
''',
|
||||||
|
'description': '''
|
||||||
|
Support Odoo 16, Enterprise and Community Edition
|
||||||
|
need to setup odoo.conf, add follow:
|
||||||
|
server_wide_modules = web,app_common
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3. Multi-language Support.
|
||||||
|
4. Multi-Company Support.
|
||||||
|
5. Support Odoo 16, Enterprise and Community Edition
|
||||||
|
==========
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3. 多语言支持
|
||||||
|
4. 多公司支持
|
||||||
|
5. Odoo 16, 企业版,社区版,多版本支持
|
||||||
|
''',
|
||||||
|
'depends': [
|
||||||
|
'mail',
|
||||||
|
'web',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/ir_cron_views.xml',
|
||||||
|
# 'report/.xml',
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
'static/src/xml/*.xml',
|
||||||
|
],
|
||||||
|
'demo': [],
|
||||||
|
# 'pre_init_hook': 'pre_init_hook',
|
||||||
|
# 'post_init_hook': 'post_init_hook',
|
||||||
|
# 'uninstall_hook': 'uninstall_hook',
|
||||||
|
'installable': True,
|
||||||
|
'application': True,
|
||||||
|
'auto_install': True,
|
||||||
|
}
|
||||||
3
app_common/controllers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import main
|
||||||
60
app_common/controllers/main.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
import requests
|
||||||
|
from math import radians, cos, sin, asin, sqrt
|
||||||
|
|
||||||
|
from ..lib.user_agents import parse
|
||||||
|
from ..models.base import get_ua_type
|
||||||
|
|
||||||
|
|
||||||
|
from odoo import api, http, SUPERUSER_ID, _
|
||||||
|
from odoo import http, exceptions
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class AppController(http.Controller):
|
||||||
|
|
||||||
|
def get_image_from_url(self, url):
|
||||||
|
if not url:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
response = requests.get(url) # 将这个图片保存在内存
|
||||||
|
except Exception as e:
|
||||||
|
return None
|
||||||
|
# 返回这个图片的base64编码
|
||||||
|
return base64.b64encode(BytesIO(response.content).read())
|
||||||
|
|
||||||
|
@http.route(['/my/ua', '/wxa/ua', '/web/ua', '/web/ua/show'], auth='public', methods=['GET'])
|
||||||
|
def app_ua_show(self):
|
||||||
|
# https://github.com/selwin/python-user-agents
|
||||||
|
ua_string = request.httprequest.headers.get('User-Agent')
|
||||||
|
user_agent = parse(ua_string)
|
||||||
|
ua_type = get_ua_type()
|
||||||
|
ustr = "Request UA: <br/> %s <br/>Parse UA: <br/>%s <br/>UA Type:<br/>%s <br/>" % (ua_string, str(user_agent), ua_type)
|
||||||
|
return request.make_response(ustr, [('Content-Type', 'text/html')])
|
||||||
|
|
||||||
|
def get_ua_type(self):
|
||||||
|
return get_ua_type()
|
||||||
|
|
||||||
|
def haversine(lon1, lat1, lon2, lat2):
|
||||||
|
# 计算地图上两点的距离
|
||||||
|
# in:经度1,纬度1,经度2,纬度2 (十进制度数)
|
||||||
|
# out: 距离(米)
|
||||||
|
"""
|
||||||
|
Calculate the great circle distance between two points
|
||||||
|
on the earth (specified in decimal degrees)
|
||||||
|
"""
|
||||||
|
# 将十进制度数转化为弧度
|
||||||
|
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
|
||||||
|
|
||||||
|
# haversine公式
|
||||||
|
dlon = lon2 - lon1
|
||||||
|
dlat = lat2 - lat1
|
||||||
|
a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
|
||||||
|
c = 2 * asin(sqrt(a))
|
||||||
|
r = 6371 # 地球平均半径,单位为公里
|
||||||
|
return c * r * 1000
|
||||||
32
app_common/hooks.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 2018-10-12
|
||||||
|
# author: 欧度智能,https://www.odooai.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of odooai
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||||
|
|
||||||
|
# Odoo在线中文用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.odooai.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.odooai.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID, _
|
||||||
|
|
||||||
|
|
||||||
|
def pre_init_hook(cr):
|
||||||
|
pass
|
||||||
|
# cr.execute("")
|
||||||
|
|
||||||
|
def post_init_hook(cr, registry):
|
||||||
|
pass
|
||||||
|
# cr.execute("")
|
||||||
|
|
||||||
|
def uninstall_hook(cr, registry):
|
||||||
|
pass
|
||||||
|
# cr.execute("")
|
||||||
|
|
||||||
29
app_common/i18n/zh_CN.po
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 10.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2018-01-08 14:28+0000\n"
|
||||||
|
"PO-Revision-Date: 2018-01-08 14:28+0000\n"
|
||||||
|
"Last-Translator: <>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: app_common
|
||||||
|
#: model:ir.model.fields,field_description:app_common.field_ir_cron__trigger_user_id
|
||||||
|
msgid "Last Trigger User"
|
||||||
|
msgstr "手动运行用户"
|
||||||
|
|
||||||
|
#. module: app_common
|
||||||
|
#: model:ir.model,name:app_common.model_ir_cron
|
||||||
|
msgid "Scheduled Actions"
|
||||||
|
msgstr "安排的动作"
|
||||||
|
|
||||||
|
#. module: app_common
|
||||||
|
#: model:ir.model,name:app_common.model_ir_ui_view
|
||||||
|
msgid "View"
|
||||||
|
msgstr "查看"
|
||||||
1
app_common/lib/ua_parser/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VERSION = (0, 10, 0)
|
||||||
7406
app_common/lib/ua_parser/_regexes.py
Normal file
544
app_common/lib/ua_parser/user_agent_parser.py
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
# Copyright 2009 Google Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the 'License')
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Python implementation of the UA parser."""
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
__author__ = "Lindsey Simon <elsigh@gmail.com>"
|
||||||
|
|
||||||
|
|
||||||
|
class UserAgentParser(object):
|
||||||
|
def __init__(
|
||||||
|
self, pattern, family_replacement=None, v1_replacement=None, v2_replacement=None
|
||||||
|
):
|
||||||
|
"""Initialize UserAgentParser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: a regular expression string
|
||||||
|
family_replacement: a string to override the matched family (optional)
|
||||||
|
v1_replacement: a string to override the matched v1 (optional)
|
||||||
|
v2_replacement: a string to override the matched v2 (optional)
|
||||||
|
"""
|
||||||
|
self.pattern = pattern
|
||||||
|
self.user_agent_re = re.compile(self.pattern)
|
||||||
|
self.family_replacement = family_replacement
|
||||||
|
self.v1_replacement = v1_replacement
|
||||||
|
self.v2_replacement = v2_replacement
|
||||||
|
|
||||||
|
def MatchSpans(self, user_agent_string):
|
||||||
|
match_spans = []
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
match_spans = [
|
||||||
|
match.span(group_index) for group_index in range(1, match.lastindex + 1)
|
||||||
|
]
|
||||||
|
return match_spans
|
||||||
|
|
||||||
|
def Parse(self, user_agent_string):
|
||||||
|
family, v1, v2, v3 = None, None, None, None
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
if self.family_replacement:
|
||||||
|
if re.search(r"\$1", self.family_replacement):
|
||||||
|
family = re.sub(r"\$1", match.group(1), self.family_replacement)
|
||||||
|
else:
|
||||||
|
family = self.family_replacement
|
||||||
|
else:
|
||||||
|
family = match.group(1)
|
||||||
|
|
||||||
|
if self.v1_replacement:
|
||||||
|
v1 = self.v1_replacement
|
||||||
|
elif match.lastindex and match.lastindex >= 2:
|
||||||
|
v1 = match.group(2) or None
|
||||||
|
|
||||||
|
if self.v2_replacement:
|
||||||
|
v2 = self.v2_replacement
|
||||||
|
elif match.lastindex and match.lastindex >= 3:
|
||||||
|
v2 = match.group(3) or None
|
||||||
|
|
||||||
|
if match.lastindex and match.lastindex >= 4:
|
||||||
|
v3 = match.group(4) or None
|
||||||
|
|
||||||
|
return family, v1, v2, v3
|
||||||
|
|
||||||
|
|
||||||
|
class OSParser(object):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pattern,
|
||||||
|
os_replacement=None,
|
||||||
|
os_v1_replacement=None,
|
||||||
|
os_v2_replacement=None,
|
||||||
|
os_v3_replacement=None,
|
||||||
|
os_v4_replacement=None,
|
||||||
|
):
|
||||||
|
"""Initialize UserAgentParser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: a regular expression string
|
||||||
|
os_replacement: a string to override the matched os (optional)
|
||||||
|
os_v1_replacement: a string to override the matched v1 (optional)
|
||||||
|
os_v2_replacement: a string to override the matched v2 (optional)
|
||||||
|
os_v3_replacement: a string to override the matched v3 (optional)
|
||||||
|
os_v4_replacement: a string to override the matched v4 (optional)
|
||||||
|
"""
|
||||||
|
self.pattern = pattern
|
||||||
|
self.user_agent_re = re.compile(self.pattern)
|
||||||
|
self.os_replacement = os_replacement
|
||||||
|
self.os_v1_replacement = os_v1_replacement
|
||||||
|
self.os_v2_replacement = os_v2_replacement
|
||||||
|
self.os_v3_replacement = os_v3_replacement
|
||||||
|
self.os_v4_replacement = os_v4_replacement
|
||||||
|
|
||||||
|
def MatchSpans(self, user_agent_string):
|
||||||
|
match_spans = []
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
match_spans = [
|
||||||
|
match.span(group_index) for group_index in range(1, match.lastindex + 1)
|
||||||
|
]
|
||||||
|
return match_spans
|
||||||
|
|
||||||
|
def Parse(self, user_agent_string):
|
||||||
|
os, os_v1, os_v2, os_v3, os_v4 = None, None, None, None, None
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
if self.os_replacement:
|
||||||
|
os = MultiReplace(self.os_replacement, match)
|
||||||
|
elif match.lastindex:
|
||||||
|
os = match.group(1)
|
||||||
|
|
||||||
|
if self.os_v1_replacement:
|
||||||
|
os_v1 = MultiReplace(self.os_v1_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 2:
|
||||||
|
os_v1 = match.group(2)
|
||||||
|
|
||||||
|
if self.os_v2_replacement:
|
||||||
|
os_v2 = MultiReplace(self.os_v2_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 3:
|
||||||
|
os_v2 = match.group(3)
|
||||||
|
|
||||||
|
if self.os_v3_replacement:
|
||||||
|
os_v3 = MultiReplace(self.os_v3_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 4:
|
||||||
|
os_v3 = match.group(4)
|
||||||
|
|
||||||
|
if self.os_v4_replacement:
|
||||||
|
os_v4 = MultiReplace(self.os_v4_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 5:
|
||||||
|
os_v4 = match.group(5)
|
||||||
|
|
||||||
|
return os, os_v1, os_v2, os_v3, os_v4
|
||||||
|
|
||||||
|
|
||||||
|
def MultiReplace(string, match):
|
||||||
|
def _repl(m):
|
||||||
|
index = int(m.group(1)) - 1
|
||||||
|
group = match.groups()
|
||||||
|
if index < len(group):
|
||||||
|
return group[index]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
_string = re.sub(r"\$(\d)", _repl, string)
|
||||||
|
_string = re.sub(r"^\s+|\s+$", "", _string)
|
||||||
|
if _string == "":
|
||||||
|
return None
|
||||||
|
return _string
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceParser(object):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pattern,
|
||||||
|
regex_flag=None,
|
||||||
|
device_replacement=None,
|
||||||
|
brand_replacement=None,
|
||||||
|
model_replacement=None,
|
||||||
|
):
|
||||||
|
"""Initialize UserAgentParser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: a regular expression string
|
||||||
|
device_replacement: a string to override the matched device (optional)
|
||||||
|
"""
|
||||||
|
self.pattern = pattern
|
||||||
|
if regex_flag == "i":
|
||||||
|
self.user_agent_re = re.compile(self.pattern, re.IGNORECASE)
|
||||||
|
else:
|
||||||
|
self.user_agent_re = re.compile(self.pattern)
|
||||||
|
self.device_replacement = device_replacement
|
||||||
|
self.brand_replacement = brand_replacement
|
||||||
|
self.model_replacement = model_replacement
|
||||||
|
|
||||||
|
def MatchSpans(self, user_agent_string):
|
||||||
|
match_spans = []
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
match_spans = [
|
||||||
|
match.span(group_index) for group_index in range(1, match.lastindex + 1)
|
||||||
|
]
|
||||||
|
return match_spans
|
||||||
|
|
||||||
|
def Parse(self, user_agent_string):
|
||||||
|
device, brand, model = None, None, None
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
if self.device_replacement:
|
||||||
|
device = MultiReplace(self.device_replacement, match)
|
||||||
|
else:
|
||||||
|
device = match.group(1)
|
||||||
|
|
||||||
|
if self.brand_replacement:
|
||||||
|
brand = MultiReplace(self.brand_replacement, match)
|
||||||
|
|
||||||
|
if self.model_replacement:
|
||||||
|
model = MultiReplace(self.model_replacement, match)
|
||||||
|
elif len(match.groups()) > 0:
|
||||||
|
model = match.group(1)
|
||||||
|
|
||||||
|
return device, brand, model
|
||||||
|
|
||||||
|
|
||||||
|
MAX_CACHE_SIZE = 20
|
||||||
|
_parse_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
def Parse(user_agent_string, **jsParseBits):
|
||||||
|
""" Parse all the things
|
||||||
|
Args:
|
||||||
|
user_agent_string: the full user agent string
|
||||||
|
jsParseBits: javascript override bits
|
||||||
|
Returns:
|
||||||
|
A dictionary containing all parsed bits
|
||||||
|
"""
|
||||||
|
jsParseBits = jsParseBits or {}
|
||||||
|
key = (user_agent_string, repr(jsParseBits))
|
||||||
|
cached = _parse_cache.get(key)
|
||||||
|
if cached is not None:
|
||||||
|
return cached
|
||||||
|
if len(_parse_cache) > MAX_CACHE_SIZE:
|
||||||
|
_parse_cache.clear()
|
||||||
|
v = {
|
||||||
|
"user_agent": ParseUserAgent(user_agent_string, **jsParseBits),
|
||||||
|
"os": ParseOS(user_agent_string, **jsParseBits),
|
||||||
|
"device": ParseDevice(user_agent_string, **jsParseBits),
|
||||||
|
"string": user_agent_string,
|
||||||
|
}
|
||||||
|
_parse_cache[key] = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def ParseUserAgent(user_agent_string, **jsParseBits):
|
||||||
|
""" Parses the user-agent string for user agent (browser) info.
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
jsParseBits: javascript override bits.
|
||||||
|
Returns:
|
||||||
|
A dictionary containing parsed bits.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
"js_user_agent_family" in jsParseBits
|
||||||
|
and jsParseBits["js_user_agent_family"] != ""
|
||||||
|
):
|
||||||
|
family = jsParseBits["js_user_agent_family"]
|
||||||
|
v1 = jsParseBits.get("js_user_agent_v1") or None
|
||||||
|
v2 = jsParseBits.get("js_user_agent_v2") or None
|
||||||
|
v3 = jsParseBits.get("js_user_agent_v3") or None
|
||||||
|
else:
|
||||||
|
for uaParser in USER_AGENT_PARSERS:
|
||||||
|
family, v1, v2, v3 = uaParser.Parse(user_agent_string)
|
||||||
|
if family:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Override for Chrome Frame IFF Chrome is enabled.
|
||||||
|
if "js_user_agent_string" in jsParseBits:
|
||||||
|
js_user_agent_string = jsParseBits["js_user_agent_string"]
|
||||||
|
if (
|
||||||
|
js_user_agent_string
|
||||||
|
and js_user_agent_string.find("Chrome/") > -1
|
||||||
|
and user_agent_string.find("chromeframe") > -1
|
||||||
|
):
|
||||||
|
jsOverride = {}
|
||||||
|
jsOverride = ParseUserAgent(js_user_agent_string)
|
||||||
|
family = "Chrome Frame (%s %s)" % (family, v1)
|
||||||
|
v1 = jsOverride["major"]
|
||||||
|
v2 = jsOverride["minor"]
|
||||||
|
v3 = jsOverride["patch"]
|
||||||
|
|
||||||
|
family = family or "Other"
|
||||||
|
return {
|
||||||
|
"family": family,
|
||||||
|
"major": v1 or None,
|
||||||
|
"minor": v2 or None,
|
||||||
|
"patch": v3 or None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def ParseOS(user_agent_string, **jsParseBits):
|
||||||
|
""" Parses the user-agent string for operating system info
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
jsParseBits: javascript override bits.
|
||||||
|
Returns:
|
||||||
|
A dictionary containing parsed bits.
|
||||||
|
"""
|
||||||
|
for osParser in OS_PARSERS:
|
||||||
|
os, os_v1, os_v2, os_v3, os_v4 = osParser.Parse(user_agent_string)
|
||||||
|
if os:
|
||||||
|
break
|
||||||
|
os = os or "Other"
|
||||||
|
return {
|
||||||
|
"family": os,
|
||||||
|
"major": os_v1,
|
||||||
|
"minor": os_v2,
|
||||||
|
"patch": os_v3,
|
||||||
|
"patch_minor": os_v4,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def ParseDevice(user_agent_string):
|
||||||
|
""" Parses the user-agent string for device info.
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
ua_family: The parsed user agent family name.
|
||||||
|
Returns:
|
||||||
|
A dictionary containing parsed bits.
|
||||||
|
"""
|
||||||
|
for deviceParser in DEVICE_PARSERS:
|
||||||
|
device, brand, model = deviceParser.Parse(user_agent_string)
|
||||||
|
if device:
|
||||||
|
break
|
||||||
|
|
||||||
|
if device is None:
|
||||||
|
device = "Other"
|
||||||
|
|
||||||
|
return {"family": device, "brand": brand, "model": model}
|
||||||
|
|
||||||
|
|
||||||
|
def PrettyUserAgent(family, v1=None, v2=None, v3=None):
|
||||||
|
"""Pretty user agent string."""
|
||||||
|
if v3:
|
||||||
|
if v3[0].isdigit():
|
||||||
|
return "%s %s.%s.%s" % (family, v1, v2, v3)
|
||||||
|
else:
|
||||||
|
return "%s %s.%s%s" % (family, v1, v2, v3)
|
||||||
|
elif v2:
|
||||||
|
return "%s %s.%s" % (family, v1, v2)
|
||||||
|
elif v1:
|
||||||
|
return "%s %s" % (family, v1)
|
||||||
|
return family
|
||||||
|
|
||||||
|
|
||||||
|
def PrettyOS(os, os_v1=None, os_v2=None, os_v3=None, os_v4=None):
|
||||||
|
"""Pretty os string."""
|
||||||
|
if os_v4:
|
||||||
|
return "%s %s.%s.%s.%s" % (os, os_v1, os_v2, os_v3, os_v4)
|
||||||
|
if os_v3:
|
||||||
|
if os_v3[0].isdigit():
|
||||||
|
return "%s %s.%s.%s" % (os, os_v1, os_v2, os_v3)
|
||||||
|
else:
|
||||||
|
return "%s %s.%s%s" % (os, os_v1, os_v2, os_v3)
|
||||||
|
elif os_v2:
|
||||||
|
return "%s %s.%s" % (os, os_v1, os_v2)
|
||||||
|
elif os_v1:
|
||||||
|
return "%s %s" % (os, os_v1)
|
||||||
|
return os
|
||||||
|
|
||||||
|
|
||||||
|
def ParseWithJSOverrides(
|
||||||
|
user_agent_string,
|
||||||
|
js_user_agent_string=None,
|
||||||
|
js_user_agent_family=None,
|
||||||
|
js_user_agent_v1=None,
|
||||||
|
js_user_agent_v2=None,
|
||||||
|
js_user_agent_v3=None,
|
||||||
|
):
|
||||||
|
""" backwards compatible. use one of the other Parse methods instead! """
|
||||||
|
|
||||||
|
# Override via JS properties.
|
||||||
|
if js_user_agent_family is not None and js_user_agent_family != "":
|
||||||
|
family = js_user_agent_family
|
||||||
|
v1 = None
|
||||||
|
v2 = None
|
||||||
|
v3 = None
|
||||||
|
if js_user_agent_v1 is not None:
|
||||||
|
v1 = js_user_agent_v1
|
||||||
|
if js_user_agent_v2 is not None:
|
||||||
|
v2 = js_user_agent_v2
|
||||||
|
if js_user_agent_v3 is not None:
|
||||||
|
v3 = js_user_agent_v3
|
||||||
|
else:
|
||||||
|
for parser in USER_AGENT_PARSERS:
|
||||||
|
family, v1, v2, v3 = parser.Parse(user_agent_string)
|
||||||
|
if family:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Override for Chrome Frame IFF Chrome is enabled.
|
||||||
|
if (
|
||||||
|
js_user_agent_string
|
||||||
|
and js_user_agent_string.find("Chrome/") > -1
|
||||||
|
and user_agent_string.find("chromeframe") > -1
|
||||||
|
):
|
||||||
|
family = "Chrome Frame (%s %s)" % (family, v1)
|
||||||
|
ua_dict = ParseUserAgent(js_user_agent_string)
|
||||||
|
v1 = ua_dict["major"]
|
||||||
|
v2 = ua_dict["minor"]
|
||||||
|
v3 = ua_dict["patch"]
|
||||||
|
|
||||||
|
return family or "Other", v1, v2, v3
|
||||||
|
|
||||||
|
|
||||||
|
def Pretty(family, v1=None, v2=None, v3=None):
|
||||||
|
""" backwards compatible. use PrettyUserAgent instead! """
|
||||||
|
if v3:
|
||||||
|
if v3[0].isdigit():
|
||||||
|
return "%s %s.%s.%s" % (family, v1, v2, v3)
|
||||||
|
else:
|
||||||
|
return "%s %s.%s%s" % (family, v1, v2, v3)
|
||||||
|
elif v2:
|
||||||
|
return "%s %s.%s" % (family, v1, v2)
|
||||||
|
elif v1:
|
||||||
|
return "%s %s" % (family, v1)
|
||||||
|
return family
|
||||||
|
|
||||||
|
|
||||||
|
def GetFilters(
|
||||||
|
user_agent_string,
|
||||||
|
js_user_agent_string=None,
|
||||||
|
js_user_agent_family=None,
|
||||||
|
js_user_agent_v1=None,
|
||||||
|
js_user_agent_v2=None,
|
||||||
|
js_user_agent_v3=None,
|
||||||
|
):
|
||||||
|
"""Return the optional arguments that should be saved and used to query.
|
||||||
|
|
||||||
|
js_user_agent_string is always returned if it is present. We really only need
|
||||||
|
it for Chrome Frame. However, I added it in the generally case to find other
|
||||||
|
cases when it is different. When the recording of js_user_agent_string was
|
||||||
|
added, we created new records for all new user agents.
|
||||||
|
|
||||||
|
Since we only added js_document_mode for the IE 9 preview case, it did not
|
||||||
|
cause new user agent records the way js_user_agent_string did.
|
||||||
|
|
||||||
|
js_document_mode has since been removed in favor of individual property
|
||||||
|
overrides.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
js_user_agent_string: JavaScript ua string from client-side
|
||||||
|
js_user_agent_family: This is an override for the family name to deal
|
||||||
|
with the fact that IE platform preview (for instance) cannot be
|
||||||
|
distinguished by user_agent_string, but only in javascript.
|
||||||
|
js_user_agent_v1: v1 override - see above.
|
||||||
|
js_user_agent_v2: v1 override - see above.
|
||||||
|
js_user_agent_v3: v1 override - see above.
|
||||||
|
Returns:
|
||||||
|
{js_user_agent_string: '[...]', js_family_name: '[...]', etc...}
|
||||||
|
"""
|
||||||
|
filters = {}
|
||||||
|
filterdict = {
|
||||||
|
"js_user_agent_string": js_user_agent_string,
|
||||||
|
"js_user_agent_family": js_user_agent_family,
|
||||||
|
"js_user_agent_v1": js_user_agent_v1,
|
||||||
|
"js_user_agent_v2": js_user_agent_v2,
|
||||||
|
"js_user_agent_v3": js_user_agent_v3,
|
||||||
|
}
|
||||||
|
for key, value in filterdict.items():
|
||||||
|
if value is not None and value != "":
|
||||||
|
filters[key] = value
|
||||||
|
return filters
|
||||||
|
|
||||||
|
|
||||||
|
# Build the list of user agent parsers from YAML
|
||||||
|
UA_PARSER_YAML = os.environ.get("UA_PARSER_YAML")
|
||||||
|
if UA_PARSER_YAML:
|
||||||
|
# This will raise an ImportError if missing, obviously since it's no
|
||||||
|
# longer a requirement
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try and use libyaml bindings if available since faster
|
||||||
|
from yaml import CSafeLoader as SafeLoader
|
||||||
|
except ImportError:
|
||||||
|
from yaml import SafeLoader
|
||||||
|
|
||||||
|
with open(UA_PARSER_YAML) as fp:
|
||||||
|
regexes = yaml.load(fp, Loader=SafeLoader)
|
||||||
|
|
||||||
|
USER_AGENT_PARSERS = []
|
||||||
|
for _ua_parser in regexes["user_agent_parsers"]:
|
||||||
|
_regex = _ua_parser["regex"]
|
||||||
|
|
||||||
|
_family_replacement = _ua_parser.get("family_replacement")
|
||||||
|
_v1_replacement = _ua_parser.get("v1_replacement")
|
||||||
|
_v2_replacement = _ua_parser.get("v2_replacement")
|
||||||
|
|
||||||
|
USER_AGENT_PARSERS.append(
|
||||||
|
UserAgentParser(
|
||||||
|
_regex, _family_replacement, _v1_replacement, _v2_replacement
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
OS_PARSERS = []
|
||||||
|
for _os_parser in regexes["os_parsers"]:
|
||||||
|
_regex = _os_parser["regex"]
|
||||||
|
|
||||||
|
_os_replacement = _os_parser.get("os_replacement")
|
||||||
|
_os_v1_replacement = _os_parser.get("os_v1_replacement")
|
||||||
|
_os_v2_replacement = _os_parser.get("os_v2_replacement")
|
||||||
|
_os_v3_replacement = _os_parser.get("os_v3_replacement")
|
||||||
|
_os_v4_replacement = _os_parser.get("os_v4_replacement")
|
||||||
|
|
||||||
|
OS_PARSERS.append(
|
||||||
|
OSParser(
|
||||||
|
_regex,
|
||||||
|
_os_replacement,
|
||||||
|
_os_v1_replacement,
|
||||||
|
_os_v2_replacement,
|
||||||
|
_os_v3_replacement,
|
||||||
|
_os_v4_replacement,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
DEVICE_PARSERS = []
|
||||||
|
for _device_parser in regexes["device_parsers"]:
|
||||||
|
_regex = _device_parser["regex"]
|
||||||
|
|
||||||
|
_regex_flag = _device_parser.get("regex_flag")
|
||||||
|
_device_replacement = _device_parser.get("device_replacement")
|
||||||
|
_brand_replacement = _device_parser.get("brand_replacement")
|
||||||
|
_model_replacement = _device_parser.get("model_replacement")
|
||||||
|
|
||||||
|
DEVICE_PARSERS.append(
|
||||||
|
DeviceParser(
|
||||||
|
_regex,
|
||||||
|
_regex_flag,
|
||||||
|
_device_replacement,
|
||||||
|
_brand_replacement,
|
||||||
|
_model_replacement,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Clean our our temporary vars explicitly
|
||||||
|
# so they can't be reused or imported
|
||||||
|
del regexes
|
||||||
|
del yaml
|
||||||
|
del SafeLoader
|
||||||
|
else:
|
||||||
|
# Just load our pre-compiled versions
|
||||||
|
from ._regexes import USER_AGENT_PARSERS, DEVICE_PARSERS, OS_PARSERS
|
||||||
290
app_common/lib/ua_parser/user_agent_parser_test.py
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
#!/usr/bin/python2.5
|
||||||
|
#
|
||||||
|
# Copyright 2008 Google Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the 'License')
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
"""User Agent Parser Unit Tests.
|
||||||
|
Run:
|
||||||
|
# python -m user_agent_parser_test (runs all the tests, takes awhile)
|
||||||
|
or like:
|
||||||
|
# python -m user_agent_parser_test ParseTest.testBrowserscopeStrings
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
__author__ = "slamm@google.com (Stephen Lamm)"
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import unittest
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try and use libyaml bindings if available since faster
|
||||||
|
from yaml import CSafeLoader as SafeLoader
|
||||||
|
except ImportError:
|
||||||
|
from yaml import SafeLoader
|
||||||
|
|
||||||
|
from ua_parser import user_agent_parser
|
||||||
|
|
||||||
|
TEST_RESOURCES_DIR = os.path.join(
|
||||||
|
os.path.abspath(os.path.dirname(__file__)), "../uap-core"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ParseTest(unittest.TestCase):
|
||||||
|
def testBrowserscopeStrings(self):
|
||||||
|
self.runUserAgentTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "tests/test_ua.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testBrowserscopeStringsOS(self):
|
||||||
|
self.runOSTestsFromYAML(os.path.join(TEST_RESOURCES_DIR, "tests/test_os.yaml"))
|
||||||
|
|
||||||
|
def testStringsOS(self):
|
||||||
|
self.runOSTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "test_resources/additional_os_tests.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testStringsDevice(self):
|
||||||
|
self.runDeviceTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "tests/test_device.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testMozillaStrings(self):
|
||||||
|
self.runUserAgentTestsFromYAML(
|
||||||
|
os.path.join(
|
||||||
|
TEST_RESOURCES_DIR, "test_resources/firefox_user_agent_strings.yaml"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# NOTE: The YAML file used here is one output by makePGTSComparisonYAML()
|
||||||
|
# below, as opposed to the pgts_browser_list-orig.yaml file. The -orig
|
||||||
|
# file is by no means perfect, but identifies many browsers that we
|
||||||
|
# classify as "Other". This test itself is mostly useful to know when
|
||||||
|
# somthing in UA parsing changes. An effort should be made to try and
|
||||||
|
# reconcile the differences between the two YAML files.
|
||||||
|
def testPGTSStrings(self):
|
||||||
|
self.runUserAgentTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "test_resources/pgts_browser_list.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testParseAll(self):
|
||||||
|
user_agent_string = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; fr; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5,gzip(gfe),gzip(gfe)"
|
||||||
|
expected = {
|
||||||
|
"device": {"family": "Mac", "brand": "Apple", "model": "Mac"},
|
||||||
|
"os": {
|
||||||
|
"family": "Mac OS X",
|
||||||
|
"major": "10",
|
||||||
|
"minor": "4",
|
||||||
|
"patch": None,
|
||||||
|
"patch_minor": None,
|
||||||
|
},
|
||||||
|
"user_agent": {
|
||||||
|
"family": "Firefox",
|
||||||
|
"major": "3",
|
||||||
|
"minor": "5",
|
||||||
|
"patch": "5",
|
||||||
|
},
|
||||||
|
"string": user_agent_string,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = user_agent_parser.Parse(user_agent_string)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1}> != actual<{2}>".format(
|
||||||
|
user_agent_string, expected, result
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a YAML file for manual comparsion with pgts_browser_list-orig.yaml
|
||||||
|
def makePGTSComparisonYAML(self):
|
||||||
|
import codecs
|
||||||
|
|
||||||
|
outfile = codecs.open("outfile.yaml", "w", "utf-8")
|
||||||
|
print >> outfile, "test_cases:"
|
||||||
|
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, "pgts_browser_list.yaml"))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
(family, major, minor, patch) = user_agent_parser.ParseUserAgent(
|
||||||
|
user_agent_string, **kwds
|
||||||
|
)
|
||||||
|
|
||||||
|
# Escape any double-quotes in the UA string
|
||||||
|
user_agent_string = re.sub(r'"', '\\"', user_agent_string)
|
||||||
|
print >> outfile, ' - user_agent_string: "' + user_agent_string + '"' + "\n" + ' family: "' + family + '"\n' + " major: " + (
|
||||||
|
"" if (major is None) else "'" + major + "'"
|
||||||
|
) + "\n" + " minor: " + (
|
||||||
|
"" if (minor is None) else "'" + minor + "'"
|
||||||
|
) + "\n" + " patch: " + (
|
||||||
|
"" if (patch is None) else "'" + patch + "'"
|
||||||
|
)
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
|
# Run a set of test cases from a YAML file
|
||||||
|
def runUserAgentTestsFromYAML(self, file_name):
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
# Inputs to Parse()
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
# The expected results
|
||||||
|
expected = {
|
||||||
|
"family": test_case["family"],
|
||||||
|
"major": test_case["major"],
|
||||||
|
"minor": test_case["minor"],
|
||||||
|
"patch": test_case["patch"],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result = user_agent_parser.ParseUserAgent(user_agent_string, **kwds)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1}, {2}, {3}, {4}> != actual<{5}, {6}, {7}, {8}>".format(
|
||||||
|
user_agent_string,
|
||||||
|
expected["family"],
|
||||||
|
expected["major"],
|
||||||
|
expected["minor"],
|
||||||
|
expected["patch"],
|
||||||
|
result["family"],
|
||||||
|
result["major"],
|
||||||
|
result["minor"],
|
||||||
|
result["patch"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def runOSTestsFromYAML(self, file_name):
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
# Inputs to Parse()
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
# The expected results
|
||||||
|
expected = {
|
||||||
|
"family": test_case["family"],
|
||||||
|
"major": test_case["major"],
|
||||||
|
"minor": test_case["minor"],
|
||||||
|
"patch": test_case["patch"],
|
||||||
|
"patch_minor": test_case["patch_minor"],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = user_agent_parser.ParseOS(user_agent_string, **kwds)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1} {2} {3} {4} {5}> != actual<{6} {7} {8} {9} {10}>".format(
|
||||||
|
user_agent_string,
|
||||||
|
expected["family"],
|
||||||
|
expected["major"],
|
||||||
|
expected["minor"],
|
||||||
|
expected["patch"],
|
||||||
|
expected["patch_minor"],
|
||||||
|
result["family"],
|
||||||
|
result["major"],
|
||||||
|
result["minor"],
|
||||||
|
result["patch"],
|
||||||
|
result["patch_minor"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def runDeviceTestsFromYAML(self, file_name):
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
# Inputs to Parse()
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
# The expected results
|
||||||
|
expected = {
|
||||||
|
"family": test_case["family"],
|
||||||
|
"brand": test_case["brand"],
|
||||||
|
"model": test_case["model"],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = user_agent_parser.ParseDevice(user_agent_string, **kwds)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1} {2} {3}> != actual<{4} {5} {6}>".format(
|
||||||
|
user_agent_string,
|
||||||
|
expected["family"],
|
||||||
|
expected["brand"],
|
||||||
|
expected["model"],
|
||||||
|
result["family"],
|
||||||
|
result["brand"],
|
||||||
|
result["model"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GetFiltersTest(unittest.TestCase):
|
||||||
|
def testGetFiltersNoMatchesGiveEmptyDict(self):
|
||||||
|
user_agent_string = "foo"
|
||||||
|
filters = user_agent_parser.GetFilters(
|
||||||
|
user_agent_string, js_user_agent_string=None
|
||||||
|
)
|
||||||
|
self.assertEqual({}, filters)
|
||||||
|
|
||||||
|
def testGetFiltersJsUaPassedThrough(self):
|
||||||
|
user_agent_string = "foo"
|
||||||
|
filters = user_agent_parser.GetFilters(
|
||||||
|
user_agent_string, js_user_agent_string="bar"
|
||||||
|
)
|
||||||
|
self.assertEqual({"js_user_agent_string": "bar"}, filters)
|
||||||
|
|
||||||
|
def testGetFiltersJsUserAgentFamilyAndVersions(self):
|
||||||
|
user_agent_string = (
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 8.0; "
|
||||||
|
"Windows NT 5.1; Trident/4.0; GTB6; .NET CLR 2.0.50727; "
|
||||||
|
".NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
|
||||||
|
)
|
||||||
|
filters = user_agent_parser.GetFilters(
|
||||||
|
user_agent_string, js_user_agent_string="bar", js_user_agent_family="foo"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
{"js_user_agent_string": "bar", "js_user_agent_family": "foo"}, filters
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
3
app_common/lib/user_agents/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
VERSION = (2, 2, 0)
|
||||||
|
|
||||||
|
from .parsers import parse
|
||||||
14
app_common/lib/user_agents/compat.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
PY3 = sys.version_info[0] == 3
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
string_types = str
|
||||||
|
|
||||||
|
def iteritems(d, **kw):
|
||||||
|
return iter(d.items(**kw))
|
||||||
|
else:
|
||||||
|
string_types = basestring
|
||||||
|
|
||||||
|
def iteritems(d, **kw):
|
||||||
|
return iter(d.iteritems(**kw))
|
||||||
268
app_common/lib/user_agents/parsers.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from ..ua_parser import user_agent_parser
|
||||||
|
from .compat import string_types
|
||||||
|
|
||||||
|
|
||||||
|
MOBILE_DEVICE_FAMILIES = (
|
||||||
|
'iPhone',
|
||||||
|
'iPod',
|
||||||
|
'Generic Smartphone',
|
||||||
|
'Generic Feature Phone',
|
||||||
|
'PlayStation Vita',
|
||||||
|
'iOS-Device'
|
||||||
|
)
|
||||||
|
|
||||||
|
PC_OS_FAMILIES = (
|
||||||
|
'Windows 95',
|
||||||
|
'Windows 98',
|
||||||
|
'Solaris',
|
||||||
|
)
|
||||||
|
|
||||||
|
MOBILE_OS_FAMILIES = (
|
||||||
|
'Windows Phone',
|
||||||
|
'Windows Phone OS', # Earlier versions of ua-parser returns Windows Phone OS
|
||||||
|
'Symbian OS',
|
||||||
|
'Bada',
|
||||||
|
'Windows CE',
|
||||||
|
'Windows Mobile',
|
||||||
|
'Maemo',
|
||||||
|
)
|
||||||
|
|
||||||
|
MOBILE_BROWSER_FAMILIES = (
|
||||||
|
'IE Mobile',
|
||||||
|
'Opera Mobile',
|
||||||
|
'Opera Mini',
|
||||||
|
'Chrome Mobile',
|
||||||
|
'Chrome Mobile WebView',
|
||||||
|
'Chrome Mobile iOS',
|
||||||
|
)
|
||||||
|
|
||||||
|
TABLET_DEVICE_FAMILIES = (
|
||||||
|
'iPad',
|
||||||
|
'BlackBerry Playbook',
|
||||||
|
'Blackberry Playbook', # Earlier versions of ua-parser returns "Blackberry" instead of "BlackBerry"
|
||||||
|
'Kindle',
|
||||||
|
'Kindle Fire',
|
||||||
|
'Kindle Fire HD',
|
||||||
|
'Galaxy Tab',
|
||||||
|
'Xoom',
|
||||||
|
'Dell Streak',
|
||||||
|
)
|
||||||
|
|
||||||
|
TOUCH_CAPABLE_OS_FAMILIES = (
|
||||||
|
'iOS',
|
||||||
|
'Android',
|
||||||
|
'Windows Phone',
|
||||||
|
'Windows CE',
|
||||||
|
'Windows Mobile',
|
||||||
|
'Firefox OS',
|
||||||
|
'MeeGo',
|
||||||
|
)
|
||||||
|
|
||||||
|
TOUCH_CAPABLE_DEVICE_FAMILIES = (
|
||||||
|
'BlackBerry Playbook',
|
||||||
|
'Blackberry Playbook',
|
||||||
|
'Kindle Fire',
|
||||||
|
)
|
||||||
|
|
||||||
|
EMAIL_PROGRAM_FAMILIES = set((
|
||||||
|
'Outlook',
|
||||||
|
'Windows Live Mail',
|
||||||
|
'AirMail',
|
||||||
|
'Apple Mail',
|
||||||
|
'Outlook',
|
||||||
|
'Thunderbird',
|
||||||
|
'Lightning',
|
||||||
|
'ThunderBrowse',
|
||||||
|
'Windows Live Mail',
|
||||||
|
'The Bat!',
|
||||||
|
'Lotus Notes',
|
||||||
|
'IBM Notes',
|
||||||
|
'Barca',
|
||||||
|
'MailBar',
|
||||||
|
'kmail2',
|
||||||
|
'YahooMobileMail'
|
||||||
|
))
|
||||||
|
|
||||||
|
def verify_attribute(attribute):
|
||||||
|
if isinstance(attribute, string_types) and attribute.isdigit():
|
||||||
|
return int(attribute)
|
||||||
|
|
||||||
|
return attribute
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version(major=None, minor=None, patch=None, patch_minor=None):
|
||||||
|
# Returns version number tuple, attributes will be integer if they're numbers
|
||||||
|
major = verify_attribute(major)
|
||||||
|
minor = verify_attribute(minor)
|
||||||
|
patch = verify_attribute(patch)
|
||||||
|
patch_minor = verify_attribute(patch_minor)
|
||||||
|
|
||||||
|
return tuple(
|
||||||
|
filter(lambda x: x is not None, (major, minor, patch, patch_minor))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Browser = namedtuple('Browser', ['family', 'version', 'version_string'])
|
||||||
|
|
||||||
|
|
||||||
|
def parse_browser(family, major=None, minor=None, patch=None, patch_minor=None):
|
||||||
|
# Returns a browser object
|
||||||
|
version = parse_version(major, minor, patch)
|
||||||
|
version_string = '.'.join([str(v) for v in version])
|
||||||
|
return Browser(family, version, version_string)
|
||||||
|
|
||||||
|
|
||||||
|
OperatingSystem = namedtuple('OperatingSystem', ['family', 'version', 'version_string'])
|
||||||
|
|
||||||
|
|
||||||
|
def parse_operating_system(family, major=None, minor=None, patch=None, patch_minor=None):
|
||||||
|
version = parse_version(major, minor, patch)
|
||||||
|
version_string = '.'.join([str(v) for v in version])
|
||||||
|
return OperatingSystem(family, version, version_string)
|
||||||
|
|
||||||
|
|
||||||
|
Device = namedtuple('Device', ['family', 'brand', 'model'])
|
||||||
|
|
||||||
|
|
||||||
|
def parse_device(family, brand, model):
|
||||||
|
return Device(family, brand, model)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAgent(object):
|
||||||
|
|
||||||
|
def __init__(self, user_agent_string):
|
||||||
|
ua_dict = user_agent_parser.Parse(user_agent_string)
|
||||||
|
self.ua_string = user_agent_string
|
||||||
|
self.os = parse_operating_system(**ua_dict['os'])
|
||||||
|
self.browser = parse_browser(**ua_dict['user_agent'])
|
||||||
|
self.device = parse_device(**ua_dict['device'])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{device} / {os} / {browser}".format(
|
||||||
|
device=self.get_device(),
|
||||||
|
os=self.get_os(),
|
||||||
|
browser=self.get_browser()
|
||||||
|
)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(str(self))
|
||||||
|
|
||||||
|
def _is_android_tablet(self):
|
||||||
|
# Newer Android tablets don't have "Mobile" in their user agent string,
|
||||||
|
# older ones like Galaxy Tab still have "Mobile" though they're not
|
||||||
|
if ('Mobile Safari' not in self.ua_string and
|
||||||
|
self.browser.family != "Firefox Mobile"):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_blackberry_touch_capable_device(self):
|
||||||
|
# A helper to determine whether a BB phone has touch capabilities
|
||||||
|
# Blackberry Bold Touch series begins with 99XX
|
||||||
|
if 'Blackberry 99' in self.device.family:
|
||||||
|
return True
|
||||||
|
if 'Blackberry 95' in self.device.family: # BB Storm devices
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_device(self):
|
||||||
|
return self.is_pc and "PC" or self.device.family
|
||||||
|
|
||||||
|
def get_os(self):
|
||||||
|
return ("%s %s" % (self.os.family, self.os.version_string)).strip()
|
||||||
|
|
||||||
|
def get_browser(self):
|
||||||
|
return ("%s %s" % (self.browser.family, self.browser.version_string)).strip()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_tablet(self):
|
||||||
|
if self.device.family in TABLET_DEVICE_FAMILIES:
|
||||||
|
return True
|
||||||
|
if (self.os.family == 'Android' and self._is_android_tablet()):
|
||||||
|
return True
|
||||||
|
if self.os.family == 'Windows' and self.os.version_string.startswith('RT'):
|
||||||
|
return True
|
||||||
|
if self.os.family == 'Firefox OS' and 'Mobile' not in self.browser.family:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_mobile(self):
|
||||||
|
# First check for mobile device and mobile browser families
|
||||||
|
if self.device.family in MOBILE_DEVICE_FAMILIES:
|
||||||
|
return True
|
||||||
|
if self.browser.family in MOBILE_BROWSER_FAMILIES:
|
||||||
|
return True
|
||||||
|
# Device is considered Mobile OS is Android and not tablet
|
||||||
|
# This is not fool proof but would have to suffice for now
|
||||||
|
if ((self.os.family == 'Android' or self.os.family == 'Firefox OS')
|
||||||
|
and not self.is_tablet):
|
||||||
|
return True
|
||||||
|
if self.os.family == 'BlackBerry OS' and self.device.family != 'Blackberry Playbook':
|
||||||
|
return True
|
||||||
|
if self.os.family in MOBILE_OS_FAMILIES:
|
||||||
|
return True
|
||||||
|
# TODO: remove after https://github.com/tobie/ua-parser/issues/126 is closed
|
||||||
|
if 'J2ME' in self.ua_string or 'MIDP' in self.ua_string:
|
||||||
|
return True
|
||||||
|
# This is here mainly to detect Google's Mobile Spider
|
||||||
|
if 'iPhone;' in self.ua_string:
|
||||||
|
return True
|
||||||
|
if 'Googlebot-Mobile' in self.ua_string:
|
||||||
|
return True
|
||||||
|
# Mobile Spiders should be identified as mobile
|
||||||
|
if self.device.family == 'Spider' and 'Mobile' in self.browser.family:
|
||||||
|
return True
|
||||||
|
# Nokia mobile
|
||||||
|
if 'NokiaBrowser' in self.ua_string and 'Mobile' in self.ua_string:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_touch_capable(self):
|
||||||
|
# TODO: detect touch capable Nokia devices
|
||||||
|
if self.os.family in TOUCH_CAPABLE_OS_FAMILIES:
|
||||||
|
return True
|
||||||
|
if self.device.family in TOUCH_CAPABLE_DEVICE_FAMILIES:
|
||||||
|
return True
|
||||||
|
if self.os.family == 'Windows':
|
||||||
|
if self.os.version_string.startswith(('RT', 'CE')):
|
||||||
|
return True
|
||||||
|
if self.os.version_string.startswith('8') and 'Touch' in self.ua_string:
|
||||||
|
return True
|
||||||
|
if 'BlackBerry' in self.os.family and self._is_blackberry_touch_capable_device():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_pc(self):
|
||||||
|
# Returns True for "PC" devices (Windows, Mac and Linux)
|
||||||
|
if 'Windows NT' in self.ua_string or self.os.family in PC_OS_FAMILIES or \
|
||||||
|
self.os.family == 'Windows' and self.os.version_string == 'ME':
|
||||||
|
return True
|
||||||
|
# TODO: remove after https://github.com/tobie/ua-parser/issues/127 is closed
|
||||||
|
if self.os.family == 'Mac OS X' and 'Silk' not in self.ua_string:
|
||||||
|
return True
|
||||||
|
# Maemo has 'Linux' and 'X11' in UA, but it is not for PC
|
||||||
|
if 'Maemo' in self.ua_string:
|
||||||
|
return False
|
||||||
|
if 'Chrome OS' in self.os.family:
|
||||||
|
return True
|
||||||
|
if 'Linux' in self.ua_string and 'X11' in self.ua_string:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_bot(self):
|
||||||
|
return True if self.device.family == 'Spider' else False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_email_client(self):
|
||||||
|
if self.browser.family in EMAIL_PROGRAM_FAMILIES:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def parse(user_agent_string):
|
||||||
|
return UserAgent(user_agent_string)
|
||||||
268
app_common/lib/user_agents/tests.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ua_parser import user_agent_parser
|
||||||
|
from . import compat
|
||||||
|
from .parsers import parse
|
||||||
|
|
||||||
|
|
||||||
|
iphone_ua_string = 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B179 Safari/7534.48.3'
|
||||||
|
ipad_ua_string = 'Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10'
|
||||||
|
galaxy_tab_ua_string = 'Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'
|
||||||
|
galaxy_s3_ua_string = 'Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
|
||||||
|
kindle_fire_ua_string = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-80) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true'
|
||||||
|
playbook_ua_string = 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.0.1; en-US) AppleWebKit/535.8+ (KHTML, like Gecko) Version/7.2.0.1 Safari/535.8+'
|
||||||
|
nexus_7_ua_string = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'
|
||||||
|
windows_phone_ua_string = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; SGH-i917)'
|
||||||
|
blackberry_torch_ua_string = 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; zh-TW) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.448 Mobile Safari/534.8+'
|
||||||
|
blackberry_bold_ua_string = 'BlackBerry9700/5.0.0.862 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/331 UNTRUSTED/1.0 3gpp-gba'
|
||||||
|
blackberry_bold_touch_ua_string = 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9930; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0.241 Mobile Safari/534.11+'
|
||||||
|
windows_rt_ua_string = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)'
|
||||||
|
j2me_opera_ua_string = 'Opera/9.80 (J2ME/MIDP; Opera Mini/9.80 (J2ME/22.478; U; en) Presto/2.5.25 Version/10.54'
|
||||||
|
ie_ua_string = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)'
|
||||||
|
ie_touch_ua_string = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Touch)'
|
||||||
|
mac_safari_ua_string = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2'
|
||||||
|
windows_ie_ua_string = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
|
||||||
|
ubuntu_firefox_ua_string = 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1'
|
||||||
|
google_bot_ua_string = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
|
||||||
|
nokia_n97_ua_string = 'Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/12.0.024; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.12344'
|
||||||
|
android_firefox_aurora_ua_string = 'Mozilla/5.0 (Android; Mobile; rv:27.0) Gecko/27.0 Firefox/27.0'
|
||||||
|
thunderbird_ua_string = 'Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0 Lightning/4.0.2'
|
||||||
|
outlook_usa_string = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0; Microsoft Outlook 15.0.4420)'
|
||||||
|
chromebook_ua_string = 'Mozilla/5.0 (X11; CrOS i686 0.12.433) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.77 Safari/534.30'
|
||||||
|
|
||||||
|
iphone_ua = parse(iphone_ua_string)
|
||||||
|
ipad_ua = parse(ipad_ua_string)
|
||||||
|
galaxy_tab = parse(galaxy_tab_ua_string)
|
||||||
|
galaxy_s3_ua = parse(galaxy_s3_ua_string)
|
||||||
|
kindle_fire_ua = parse(kindle_fire_ua_string)
|
||||||
|
playbook_ua = parse(playbook_ua_string)
|
||||||
|
nexus_7_ua = parse(nexus_7_ua_string)
|
||||||
|
windows_phone_ua = parse(windows_phone_ua_string)
|
||||||
|
windows_rt_ua = parse(windows_rt_ua_string)
|
||||||
|
blackberry_torch_ua = parse(blackberry_torch_ua_string)
|
||||||
|
blackberry_bold_ua = parse(blackberry_bold_ua_string)
|
||||||
|
blackberry_bold_touch_ua = parse(blackberry_bold_touch_ua_string)
|
||||||
|
j2me_opera_ua = parse(j2me_opera_ua_string)
|
||||||
|
ie_ua = parse(ie_ua_string)
|
||||||
|
ie_touch_ua = parse(ie_touch_ua_string)
|
||||||
|
mac_safari_ua = parse(mac_safari_ua_string)
|
||||||
|
windows_ie_ua = parse(windows_ie_ua_string)
|
||||||
|
ubuntu_firefox_ua = parse(ubuntu_firefox_ua_string)
|
||||||
|
google_bot_ua = parse(google_bot_ua_string)
|
||||||
|
nokia_n97_ua = parse(nokia_n97_ua_string)
|
||||||
|
android_firefox_aurora_ua = parse(android_firefox_aurora_ua_string)
|
||||||
|
thunderbird_ua = parse(thunderbird_ua_string)
|
||||||
|
outlook_ua = parse(outlook_usa_string)
|
||||||
|
chromebook_ua = parse(chromebook_ua_string)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAgentsTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_user_agent_object_assignments(self):
|
||||||
|
ua_dict = user_agent_parser.Parse(devices['iphone']['ua_string'])
|
||||||
|
iphone_ua = devices['iphone']['user_agent']
|
||||||
|
|
||||||
|
# Ensure browser attributes are assigned correctly
|
||||||
|
self.assertEqual(iphone_ua.browser.family,
|
||||||
|
ua_dict['user_agent']['family'])
|
||||||
|
self.assertEqual(
|
||||||
|
iphone_ua.browser.version,
|
||||||
|
(int(ua_dict['user_agent']['major']),
|
||||||
|
int(ua_dict['user_agent']['minor']))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure os attributes are assigned correctly
|
||||||
|
self.assertEqual(iphone_ua.os.family, ua_dict['os']['family'])
|
||||||
|
self.assertEqual(
|
||||||
|
iphone_ua.os.version,
|
||||||
|
(int(ua_dict['os']['major']), int(ua_dict['os']['minor']))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure device attributes are assigned correctly
|
||||||
|
self.assertEqual(iphone_ua.device.family,
|
||||||
|
ua_dict['device']['family'])
|
||||||
|
|
||||||
|
def test_is_tablet_property(self):
|
||||||
|
self.assertFalse(iphone_ua.is_tablet)
|
||||||
|
self.assertFalse(galaxy_s3_ua.is_tablet)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_tablet)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_tablet)
|
||||||
|
self.assertFalse(windows_phone_ua.is_tablet)
|
||||||
|
self.assertFalse(ie_ua.is_tablet)
|
||||||
|
self.assertFalse(ie_touch_ua.is_tablet)
|
||||||
|
self.assertFalse(mac_safari_ua.is_tablet)
|
||||||
|
self.assertFalse(windows_ie_ua.is_tablet)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_tablet)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_tablet)
|
||||||
|
self.assertFalse(google_bot_ua.is_tablet)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_tablet)
|
||||||
|
self.assertTrue(windows_rt_ua.is_tablet)
|
||||||
|
self.assertTrue(ipad_ua.is_tablet)
|
||||||
|
self.assertTrue(playbook_ua.is_tablet)
|
||||||
|
self.assertTrue(kindle_fire_ua.is_tablet)
|
||||||
|
self.assertTrue(nexus_7_ua.is_tablet)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_tablet)
|
||||||
|
|
||||||
|
def test_is_mobile_property(self):
|
||||||
|
self.assertTrue(iphone_ua.is_mobile)
|
||||||
|
self.assertTrue(galaxy_s3_ua.is_mobile)
|
||||||
|
self.assertTrue(blackberry_torch_ua.is_mobile)
|
||||||
|
self.assertTrue(blackberry_bold_ua.is_mobile)
|
||||||
|
self.assertTrue(windows_phone_ua.is_mobile)
|
||||||
|
self.assertTrue(j2me_opera_ua.is_mobile)
|
||||||
|
self.assertTrue(nokia_n97_ua.is_mobile)
|
||||||
|
self.assertFalse(windows_rt_ua.is_mobile)
|
||||||
|
self.assertFalse(ipad_ua.is_mobile)
|
||||||
|
self.assertFalse(playbook_ua.is_mobile)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_mobile)
|
||||||
|
self.assertFalse(nexus_7_ua.is_mobile)
|
||||||
|
self.assertFalse(ie_ua.is_mobile)
|
||||||
|
self.assertFalse(ie_touch_ua.is_mobile)
|
||||||
|
self.assertFalse(mac_safari_ua.is_mobile)
|
||||||
|
self.assertFalse(windows_ie_ua.is_mobile)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_mobile)
|
||||||
|
self.assertFalse(google_bot_ua.is_mobile)
|
||||||
|
self.assertTrue(android_firefox_aurora_ua.is_mobile)
|
||||||
|
|
||||||
|
def test_is_touch_property(self):
|
||||||
|
self.assertTrue(iphone_ua.is_touch_capable)
|
||||||
|
self.assertTrue(galaxy_s3_ua.is_touch_capable)
|
||||||
|
self.assertTrue(ipad_ua.is_touch_capable)
|
||||||
|
self.assertTrue(playbook_ua.is_touch_capable)
|
||||||
|
self.assertTrue(kindle_fire_ua.is_touch_capable)
|
||||||
|
self.assertTrue(nexus_7_ua.is_touch_capable)
|
||||||
|
self.assertTrue(windows_phone_ua.is_touch_capable)
|
||||||
|
self.assertTrue(ie_touch_ua.is_touch_capable)
|
||||||
|
self.assertTrue(blackberry_bold_touch_ua.is_mobile)
|
||||||
|
self.assertTrue(blackberry_torch_ua.is_mobile)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_touch_capable)
|
||||||
|
self.assertFalse(ie_ua.is_touch_capable)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_touch_capable)
|
||||||
|
self.assertFalse(mac_safari_ua.is_touch_capable)
|
||||||
|
self.assertFalse(windows_ie_ua.is_touch_capable)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_touch_capable)
|
||||||
|
self.assertFalse(google_bot_ua.is_touch_capable)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_touch_capable)
|
||||||
|
self.assertTrue(android_firefox_aurora_ua.is_touch_capable)
|
||||||
|
|
||||||
|
def test_is_pc(self):
|
||||||
|
self.assertFalse(iphone_ua.is_pc)
|
||||||
|
self.assertFalse(galaxy_s3_ua.is_pc)
|
||||||
|
self.assertFalse(ipad_ua.is_pc)
|
||||||
|
self.assertFalse(playbook_ua.is_pc)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_pc)
|
||||||
|
self.assertFalse(nexus_7_ua.is_pc)
|
||||||
|
self.assertFalse(windows_phone_ua.is_pc)
|
||||||
|
self.assertFalse(blackberry_bold_touch_ua.is_pc)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_pc)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_pc)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_pc)
|
||||||
|
self.assertFalse(google_bot_ua.is_pc)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_pc)
|
||||||
|
self.assertTrue(mac_safari_ua.is_pc)
|
||||||
|
self.assertTrue(windows_ie_ua.is_pc)
|
||||||
|
self.assertTrue(ubuntu_firefox_ua.is_pc)
|
||||||
|
self.assertTrue(ie_touch_ua.is_pc)
|
||||||
|
self.assertTrue(ie_ua.is_pc)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_pc)
|
||||||
|
self.assertTrue(chromebook_ua.is_pc)
|
||||||
|
|
||||||
|
def test_is_bot(self):
|
||||||
|
self.assertTrue(google_bot_ua.is_bot)
|
||||||
|
self.assertFalse(iphone_ua.is_bot)
|
||||||
|
self.assertFalse(galaxy_s3_ua.is_bot)
|
||||||
|
self.assertFalse(ipad_ua.is_bot)
|
||||||
|
self.assertFalse(playbook_ua.is_bot)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_bot)
|
||||||
|
self.assertFalse(nexus_7_ua.is_bot)
|
||||||
|
self.assertFalse(windows_phone_ua.is_bot)
|
||||||
|
self.assertFalse(blackberry_bold_touch_ua.is_bot)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_bot)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_bot)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_bot)
|
||||||
|
self.assertFalse(mac_safari_ua.is_bot)
|
||||||
|
self.assertFalse(windows_ie_ua.is_bot)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_bot)
|
||||||
|
self.assertFalse(ie_touch_ua.is_bot)
|
||||||
|
self.assertFalse(ie_ua.is_bot)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_bot)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_bot)
|
||||||
|
|
||||||
|
def test_is_email_client(self):
|
||||||
|
self.assertTrue(thunderbird_ua.is_email_client)
|
||||||
|
self.assertTrue(outlook_ua.is_email_client)
|
||||||
|
self.assertFalse(playbook_ua.is_email_client)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_email_client)
|
||||||
|
self.assertFalse(nexus_7_ua.is_email_client)
|
||||||
|
self.assertFalse(windows_phone_ua.is_email_client)
|
||||||
|
self.assertFalse(blackberry_bold_touch_ua.is_email_client)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_email_client)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_email_client)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_email_client)
|
||||||
|
self.assertFalse(mac_safari_ua.is_email_client)
|
||||||
|
self.assertFalse(windows_ie_ua.is_email_client)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_email_client)
|
||||||
|
self.assertFalse(ie_touch_ua.is_email_client)
|
||||||
|
self.assertFalse(ie_ua.is_email_client)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_email_client)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_email_client)
|
||||||
|
|
||||||
|
|
||||||
|
def test_strings(self):
|
||||||
|
self.assertEqual(str(iphone_ua), "iPhone / iOS 5.1 / Mobile Safari 5.1")
|
||||||
|
self.assertEqual(str(ipad_ua), "iPad / iOS 3.2 / Mobile Safari 4.0.4")
|
||||||
|
self.assertEqual(str(galaxy_tab), "Samsung SCH-I800 / Android 2.2 / Android 2.2")
|
||||||
|
self.assertEqual(str(galaxy_s3_ua), "Samsung GT-I9300 / Android 4.0.4 / Android 4.0.4")
|
||||||
|
self.assertEqual(str(kindle_fire_ua), "Kindle / Android / Amazon Silk 1.1.0-80")
|
||||||
|
self.assertEqual(str(playbook_ua), "BlackBerry Playbook / BlackBerry Tablet OS 2.0.1 / BlackBerry WebKit 2.0.1")
|
||||||
|
self.assertEqual(str(nexus_7_ua), "Asus Nexus 7 / Android 4.1.1 / Chrome 18.0.1025")
|
||||||
|
self.assertEqual(str(windows_phone_ua), "Samsung SGH-i917 / Windows Phone 7.5 / IE Mobile 9.0")
|
||||||
|
self.assertEqual(str(windows_rt_ua), "PC / Windows RT / IE 10.0")
|
||||||
|
self.assertEqual(str(blackberry_torch_ua), "BlackBerry 9800 / BlackBerry OS 6.0.0 / BlackBerry WebKit 6.0.0")
|
||||||
|
self.assertEqual(str(blackberry_bold_ua), "BlackBerry 9700 / BlackBerry OS 5.0.0 / BlackBerry 9700")
|
||||||
|
self.assertEqual(str(blackberry_bold_touch_ua), "BlackBerry 9930 / BlackBerry OS 7.0.0 / BlackBerry WebKit 7.0.0")
|
||||||
|
self.assertEqual(str(j2me_opera_ua), "Generic Feature Phone / Other / Opera Mini 9.80")
|
||||||
|
self.assertEqual(str(ie_ua), "PC / Windows 8 / IE 10.0")
|
||||||
|
self.assertEqual(str(ie_touch_ua), "PC / Windows 8 / IE 10.0")
|
||||||
|
self.assertEqual(str(mac_safari_ua), "PC / Mac OS X 10.6.8 / WebKit Nightly 537.13")
|
||||||
|
self.assertEqual(str(windows_ie_ua), "PC / Windows 7 / IE 9.0")
|
||||||
|
self.assertEqual(str(ubuntu_firefox_ua), "PC / Ubuntu / Firefox 15.0.1")
|
||||||
|
self.assertEqual(str(google_bot_ua), "Spider / Other / Googlebot 2.1")
|
||||||
|
self.assertEqual(str(nokia_n97_ua), "Nokia N97 / Symbian OS 9.4 / Nokia Browser 7.1.12344")
|
||||||
|
self.assertEqual(str(android_firefox_aurora_ua), "Generic Smartphone / Android / Firefox Mobile 27.0")
|
||||||
|
|
||||||
|
def test_unicode_strings(self):
|
||||||
|
try:
|
||||||
|
# Python 2
|
||||||
|
unicode_ua_str = unicode(devices['iphone']['user_agent'])
|
||||||
|
self.assertEqual(unicode_ua_str,
|
||||||
|
u"iPhone / iOS 5.1 / Mobile Safari 5.1")
|
||||||
|
self.assertTrue(isinstance(unicode_ua_str, unicode))
|
||||||
|
except NameError:
|
||||||
|
# Python 3
|
||||||
|
unicode_ua_str = str(devices['iphone']['user_agent'])
|
||||||
|
self.assertEqual(unicode_ua_str,
|
||||||
|
"iPhone / iOS 5.1 / Mobile Safari 5.1")
|
||||||
|
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'devices.json')) as f:
|
||||||
|
devices = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrapper(items):
|
||||||
|
def test_func(self):
|
||||||
|
attrs = ('is_bot', 'is_mobile',
|
||||||
|
'is_pc', 'is_tablet', 'is_touch_capable')
|
||||||
|
for attr in attrs:
|
||||||
|
self.assertEqual(
|
||||||
|
getattr(items['user_agent'], attr), items[attr], msg=attr)
|
||||||
|
# Temporarily commenting this out since UserAgent.device
|
||||||
|
# may return different string depending ua-parser version
|
||||||
|
# self.assertEqual(str(items['user_agent']), items['str'])
|
||||||
|
return test_func
|
||||||
|
|
||||||
|
for device, items in compat.iteritems(devices):
|
||||||
|
items['user_agent'] = parse(items['ua_string'])
|
||||||
|
setattr(UserAgentsTest, 'test_' + device, test_wrapper(items))
|
||||||
37
app_common/models/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of odooai.cn. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
# Created on 2023-02-02
|
||||||
|
# author: 欧度智能,http://www.odooai.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of odooai
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
# Odoo12在线用户手册(长期更新)
|
||||||
|
# http://www.odooai.cn/documentation/user/12.0/en/index.html
|
||||||
|
|
||||||
|
# Odoo12在线开发者手册(长期更新)
|
||||||
|
# http://www.odooai.cn/documentation/12.0/index.html
|
||||||
|
|
||||||
|
# Odoo10在线中文用户手册(长期更新)
|
||||||
|
# http://www.odooai.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# http://www.odooai.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# http://www.odooai.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
from . import base
|
||||||
|
from . import ir_ui_view
|
||||||
|
from . import ir_cron
|
||||||
|
from . import res_users
|
||||||
|
from . import ir_mail_server
|
||||||
|
from . import mail_mail
|
||||||
|
from . import ir_http
|
||||||
|
from . import app_import
|
||||||
|
from . import res_partner
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
160
app_common/models/base.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
from datetime import date, datetime, time
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# 常规的排除的fields
|
||||||
|
EXCLU_FIELDS = [
|
||||||
|
'__last_update',
|
||||||
|
'access_token',
|
||||||
|
'access_url',
|
||||||
|
'access_warning',
|
||||||
|
'activity_date_deadline',
|
||||||
|
'activity_exception_decoration',
|
||||||
|
'activity_exception_icon',
|
||||||
|
'activity_ids',
|
||||||
|
'activity_state',
|
||||||
|
'activity_summary',
|
||||||
|
'activity_type_id',
|
||||||
|
'activity_user_id',
|
||||||
|
'display_name',
|
||||||
|
'message_attachment_count',
|
||||||
|
'message_channel_ids',
|
||||||
|
'message_follower_ids',
|
||||||
|
'message_has_error',
|
||||||
|
'message_has_error_counter',
|
||||||
|
'message_has_sms_error',
|
||||||
|
'message_ids',
|
||||||
|
'message_is_follower',
|
||||||
|
'message_main_attachment_id',
|
||||||
|
'message_needaction',
|
||||||
|
'message_needaction_counter',
|
||||||
|
'message_partner_ids',
|
||||||
|
'message_unread',
|
||||||
|
'message_unread_counter',
|
||||||
|
'website_message_ids',
|
||||||
|
'write_date',
|
||||||
|
'write_uid',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Base(models.AbstractModel):
|
||||||
|
_inherit = 'base'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_normal_fields(self):
|
||||||
|
f_list = []
|
||||||
|
for k, v in self._fields.items():
|
||||||
|
if k not in EXCLU_FIELDS:
|
||||||
|
f_list.append(k)
|
||||||
|
return f_list
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _app_get_m2o_default(self, fieldname, domain=[]):
|
||||||
|
if hasattr(self, fieldname) and self._fields[fieldname].type == 'many2one':
|
||||||
|
if self._context.get(fieldname) or self._context.get('default_%s' % fieldname):
|
||||||
|
return self._context.get(fieldname) or self._context.get('default_%s' % fieldname)
|
||||||
|
else:
|
||||||
|
if not domain:
|
||||||
|
domain = self._fields[fieldname].domain or []
|
||||||
|
rec = self.env[self._fields[fieldname].comodel_name].sudo().search(domain, limit=1)
|
||||||
|
return rec.id if rec else False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _app_dt2local(self, value, return_format=DEFAULT_SERVER_DATETIME_FORMAT):
|
||||||
|
"""
|
||||||
|
将value中时间,按格式转为用户本地时间.注意只处理in str为字符串类型,如果是时间类型直接用 datetime.now(tz)
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
return value
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
value = value.strftime(return_format)
|
||||||
|
dt = datetime.strptime(value, return_format)
|
||||||
|
user_tz = pytz.timezone(self.env.user.tz or 'Etc/GMT-8')
|
||||||
|
_logger.warning('============= user2 tz: %s' % user_tz)
|
||||||
|
dt = dt.replace(tzinfo=pytz.timezone('UTC'))
|
||||||
|
return dt.astimezone(user_tz).strftime(return_format)
|
||||||
|
|
||||||
|
def _app_dt2utc(self, value, return_format=DEFAULT_SERVER_DATETIME_FORMAT):
|
||||||
|
"""
|
||||||
|
将value中用户本地时间,按格式转为UTC时间,输出 str
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
return value
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
value = value.strftime(return_format)
|
||||||
|
dt = datetime.strptime(value, return_format)
|
||||||
|
pytz_timezone = pytz.timezone('Etc/GMT+8')
|
||||||
|
dt = dt.replace(tzinfo=pytz.timezone('UTC'))
|
||||||
|
return dt.astimezone(pytz_timezone).strftime(return_format)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_image_from_url(self, url):
|
||||||
|
# 返回这个图片的base64编码
|
||||||
|
return get_image_from_url(url)
|
||||||
|
|
||||||
|
def get_ua_type(self):
|
||||||
|
return get_ua_type()
|
||||||
|
|
||||||
|
def get_image_from_url(url):
|
||||||
|
if not url:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
response = requests.get(url, timeout=5)
|
||||||
|
except Exception as e:
|
||||||
|
return None
|
||||||
|
# 返回这个图片的base64编码
|
||||||
|
return base64.b64encode(BytesIO(response.content).read())
|
||||||
|
|
||||||
|
def get_ua_type():
|
||||||
|
ua = request.httprequest.headers.get('User-Agent')
|
||||||
|
# 临时用 agent 处理,后续要前端中正确处理或者都从后台来
|
||||||
|
# 微信浏览器
|
||||||
|
# MicroMessenger: Mozilla/5.0 (Linux; Android 10; ELE-AL00 Build/HUAWEIELE-AL00; wv)
|
||||||
|
# AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120
|
||||||
|
# MQQBrowser/6.2 TBS/045525 Mobile Safari/537.36 MMWEBID/3135 MicroMessenger/8.0.2.1860(0x2800023B) Process/tools WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64
|
||||||
|
# 微信浏览器,开发工具,网页 iphone
|
||||||
|
# ,Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
|
||||||
|
# wechatdevtools/1.03.2011120 MicroMessenger/7.0.4 Language/zh_CN webview/16178807094901773
|
||||||
|
# webdebugger port/27772 token/b91f4a234b918f4e2a5d1a835a09c31e
|
||||||
|
|
||||||
|
# 微信小程序
|
||||||
|
# MicroMessenger: Mozilla/5.0 (Linux; Android 10; ELE-AL00 Build/HUAWEIELE-AL00; wv)
|
||||||
|
# AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.62 XWEB/2767 MMWEBSDK/20210302 Mobile Safari/537.36 MMWEBID/6689 MicroMessenger/8.0.2.1860(0x2800023B) Process/appbrand2 WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64
|
||||||
|
# MiniProgramEnv/android
|
||||||
|
# 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_7_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.37(0x18002529) NetType/WIFI Language/zh_CN'
|
||||||
|
# 微信浏览器,开发工具,小程序,iphone
|
||||||
|
# Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
|
||||||
|
# wechatdevtools/1.03.2011120 MicroMessenger/7.0.4 Language/zh_CN webview/
|
||||||
|
# 微信内,iphone web
|
||||||
|
# Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
|
||||||
|
# MicroMessenger/8.0.3(0x1800032a) NetType/WIFI Language/zh_CN
|
||||||
|
# 安卓app,h5
|
||||||
|
# ELE-AL00(Android/10) (cn.erpapp.o20sticks.App/13.20.12.09) Weex/0.26.0 1080x2265
|
||||||
|
|
||||||
|
# web 表示普通浏览器,后续更深入处理
|
||||||
|
utype = 'web'
|
||||||
|
# todo: 引入现成 py lib,处理企业微信
|
||||||
|
if 'MicroMessenger' in ua and 'webdebugger' not in ua \
|
||||||
|
and ('miniProgram' in ua or 'MiniProgram' in ua or 'MiniProgramEnv' in ua or 'wechatdevtools' in ua):
|
||||||
|
# 微信小程序及开发者工具
|
||||||
|
utype = 'wxapp'
|
||||||
|
elif 'MicroMessenger' in ua:
|
||||||
|
# 微信浏览器
|
||||||
|
utype = 'wxweb'
|
||||||
|
elif 'cn.erpapp.o20sticks.App' in ua:
|
||||||
|
# 安卓app
|
||||||
|
utype = 'native_android'
|
||||||
|
# _logger.warning('=========get ua %s,%s' % (utype, ua))
|
||||||
|
return utype
|
||||||
15
app_common/models/ir_cron.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import logging
|
||||||
|
from odoo import api, fields, models, modules, tools, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class IrCron(models.Model):
|
||||||
|
_inherit = "ir.cron"
|
||||||
|
|
||||||
|
trigger_user_id = fields.Many2one('res.users', string='Last Trigger User')
|
||||||
|
|
||||||
|
def method_direct_trigger(self):
|
||||||
|
self.write({'trigger_user_id': self.env.user.id})
|
||||||
|
return super(IrCron, self).method_direct_trigger()
|
||||||
17
app_common/models/ir_http.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
|
||||||
|
from odoo import models
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
|
class IrHttp(models.AbstractModel):
|
||||||
|
_inherit = 'ir.http'
|
||||||
|
|
||||||
|
def session_info(self):
|
||||||
|
result = super(IrHttp, self).session_info()
|
||||||
|
result['ua_type'] = self.get_ua_type()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
29
app_common/models/ir_mail_server.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class IrMailServer(models.Model):
|
||||||
|
_inherit = "ir.mail_server"
|
||||||
|
_order = "sequence"
|
||||||
|
|
||||||
|
# 改默认发邮件逻辑
|
||||||
|
@api.model
|
||||||
|
def send_email(self, message, mail_server_id=None, smtp_server=None, smtp_port=None,
|
||||||
|
smtp_user=None, smtp_password=None, smtp_encryption=None, smtp_debug=False,
|
||||||
|
smtp_session=None):
|
||||||
|
|
||||||
|
email_to = message['To']
|
||||||
|
|
||||||
|
# 忽略掉无效email,避免被ban
|
||||||
|
if email_to.find('no-reply@odooai.cn') != -1 or email_to.find('postmaster-odoo@odooai.cn') != -1:
|
||||||
|
pass
|
||||||
|
elif email_to.find('example.com') != -1 or email_to.find('@sunpop.cn') != -1 or email_to.find('@odooapp.cn') != -1:
|
||||||
|
_logger.error(_("=================Email to ignore: %s") % email_to)
|
||||||
|
raise AssertionError(_("Email to ignore: %s") % email_to)
|
||||||
|
|
||||||
|
return super(IrMailServer, self).send_email(message, mail_server_id, smtp_server, smtp_port,
|
||||||
|
smtp_user, smtp_password, smtp_encryption, smtp_debug,
|
||||||
|
smtp_session)
|
||||||
42
app_common/models/ir_ui_view.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, models, tools, SUPERUSER_ID
|
||||||
|
from odoo.modules.module import get_resource_path
|
||||||
|
from odoo.tools import view_validation
|
||||||
|
from odoo.tools.view_validation import _relaxng_cache, validate, _validators
|
||||||
|
from odoo.tools.safe_eval import safe_eval
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def app_relaxng(view_type):
|
||||||
|
""" Return a validator for the given view type, or None. """
|
||||||
|
if view_type not in _relaxng_cache:
|
||||||
|
# tree, search 特殊
|
||||||
|
if view_type in ['tree', 'search']:
|
||||||
|
_file = get_resource_path('app_common', 'rng', '%s_view.rng' % view_type)
|
||||||
|
else:
|
||||||
|
_file = get_resource_path('base', 'rng', '%s_view.rng' % view_type)
|
||||||
|
with tools.file_open(_file) as frng:
|
||||||
|
try:
|
||||||
|
relaxng_doc = etree.parse(frng)
|
||||||
|
_relaxng_cache[view_type] = etree.RelaxNG(relaxng_doc)
|
||||||
|
except Exception:
|
||||||
|
_logger.error('Failed to load RelaxNG XML schema for views validation')
|
||||||
|
_relaxng_cache[view_type] = None
|
||||||
|
return _relaxng_cache[view_type]
|
||||||
|
|
||||||
|
view_validation.relaxng = app_relaxng
|
||||||
|
|
||||||
|
class View(models.Model):
|
||||||
|
_inherit = 'ir.ui.view'
|
||||||
|
|
||||||
|
def __init__(self, env, ids, prefetch_ids):
|
||||||
|
# 这里应该是无必要,但为了更安全
|
||||||
|
super(View, self).__init__(env, ids, prefetch_ids)
|
||||||
|
view_validation.relaxng = app_relaxng
|
||||||
|
|
||||||
|
# todo: 有可能需要处理增加的 header等标签
|
||||||
|
# 直接重写原生方法
|
||||||
|
# def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False):
|
||||||
16
app_common/models/mail_mail.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class MailMail(models.Model):
|
||||||
|
_inherit = "mail.mail"
|
||||||
|
|
||||||
|
# 猴子补丁模式,改默认发邮件逻辑
|
||||||
|
def _send(self, auto_commit=False, raise_exception=False, smtp_session=None):
|
||||||
|
for m in self:
|
||||||
|
if m.email_to and (m.email_to.find('example.com') != -1 or m.email_to.find('@odooai.cn') != -1 or m.email_to.find('@odooapp.cn') != -1):
|
||||||
|
self = self - m
|
||||||
|
return super(MailMail, self)._send(auto_commit, raise_exception, smtp_session)
|
||||||
13
app_common/models/res_partner.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from odoo import api, fields, models, tools, _
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
def get_related_user_id(self):
|
||||||
|
self.ensure_one()
|
||||||
|
user = self.env['res.users'].sudo().with_context(active_test=False).search([('partner_id', '=', self.id)], limit=1)
|
||||||
|
return user
|
||||||
10
app_common/models/res_users.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from odoo import api, fields, models, tools, _
|
||||||
|
|
||||||
|
|
||||||
|
class ResUsers(models.Model):
|
||||||
|
_inherit = 'res.users'
|
||||||
|
|
||||||
|
login = fields.Char(index=True)
|
||||||
2
app_common/report/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
445
app_common/rng/common.rng
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0"
|
||||||
|
xmlns:a="http://relaxng.org/ns/annotation/1.0"
|
||||||
|
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||||
|
<!-- Handling of element overloading when inheriting from a base
|
||||||
|
template
|
||||||
|
-->
|
||||||
|
|
||||||
|
<rng:define name="overload">
|
||||||
|
<rng:optional>
|
||||||
|
<!--
|
||||||
|
Alter matched element with content
|
||||||
|
-->
|
||||||
|
<rng:choice>
|
||||||
|
<rng:attribute name="position">
|
||||||
|
<rng:choice>
|
||||||
|
<!-- Insert content before first child -->
|
||||||
|
<rng:value>before</rng:value>
|
||||||
|
<!-- Insert content after last child -->
|
||||||
|
<rng:value>after</rng:value>
|
||||||
|
<!-- Replace all children with content -->
|
||||||
|
<rng:value>inside</rng:value>
|
||||||
|
<!-- Replace matched element itself with content -->
|
||||||
|
<rng:value>replace</rng:value>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:attribute>
|
||||||
|
<rng:group>
|
||||||
|
<rng:attribute name="position">
|
||||||
|
<!-- Edit element attributes -->
|
||||||
|
<rng:value>attributes</rng:value>
|
||||||
|
</rng:attribute>
|
||||||
|
<rng:oneOrMore>
|
||||||
|
<rng:element name="attribute">
|
||||||
|
<rng:attribute name="name"><rng:text/></rng:attribute>
|
||||||
|
<rng:text />
|
||||||
|
</rng:element>
|
||||||
|
</rng:oneOrMore>
|
||||||
|
</rng:group>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:optional>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="modifiable">
|
||||||
|
<rng:optional>
|
||||||
|
<!-- @modifiers contains a JSON map unifying the various
|
||||||
|
modifier attributes: @readonly, @required, @invisible.
|
||||||
|
Each attribute is a key, mapped to a JSON list representing
|
||||||
|
a condition expressed as an OpenERP `domain` filter
|
||||||
|
Only some of the modifier keys make sense on some
|
||||||
|
elements, for example <filter> and <group> only support
|
||||||
|
`invisible`. -->
|
||||||
|
<rng:attribute name="modifiers"/>
|
||||||
|
</rng:optional>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="access_rights">
|
||||||
|
<rng:optional>
|
||||||
|
<rng:attribute name="groups"/>
|
||||||
|
</rng:optional>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="container">
|
||||||
|
<rng:optional><rng:attribute name="col"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
<rng:ref name="group"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="label" />
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="image"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="image">
|
||||||
|
<rng:element name="image">
|
||||||
|
<rng:attribute name="name"/>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="html">
|
||||||
|
<rng:element name="html">
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:text/>
|
||||||
|
<rng:ref name="any"/>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="label">
|
||||||
|
<rng:element name="label">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:ref name="access_rights"/>
|
||||||
|
<rng:ref name="modifiable"/>
|
||||||
|
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="align"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="nolabel"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="angle"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="fill"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="help"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="width"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="wrap"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||||
|
<rng:optional>
|
||||||
|
<!-- @for: allows to explicitely link a label to a field -->
|
||||||
|
<rng:attribute name="for"/>
|
||||||
|
</rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:text/>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
<rng:ref name="group"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="label" />
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="image"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="any">
|
||||||
|
<rng:element>
|
||||||
|
<rng:anyName/>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:attribute>
|
||||||
|
<rng:anyName/>
|
||||||
|
</rng:attribute>
|
||||||
|
<rng:text/>
|
||||||
|
<rng:ref name="any"/>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="separator">
|
||||||
|
<rng:element name="separator">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:ref name="access_rights"/>
|
||||||
|
<rng:ref name="modifiable"/>
|
||||||
|
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="rowspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="col"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="select"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="orientation"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
<rng:ref name="label" />
|
||||||
|
<rng:ref name="group" />
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="xpath">
|
||||||
|
<rng:element name="xpath">
|
||||||
|
<rng:optional><rng:attribute name="expr"/></rng:optional>
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="any"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="data">
|
||||||
|
<rng:element name="data">
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
<rng:ref name="label"/>
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="xpath"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="group"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="field">
|
||||||
|
<rng:element name="field">
|
||||||
|
<rng:attribute name="name" />
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:ref name="access_rights"/>
|
||||||
|
<rng:ref name="modifiable"/>
|
||||||
|
<rng:optional><rng:attribute name="allow_group_range_value"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="domain_filter"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="class"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="completion"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="width"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="type"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="ref"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="eval"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="search"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="model"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="use"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="on_change"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="domain"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="filter_domain"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="password"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="comparator"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="sum"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bold"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="avg"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="select"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="hierarchize"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="expand"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="group"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="color"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="groupby"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="enable_counters"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="limit"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="operator"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="nolabel"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="required"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="readonly"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="view_mode"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="widget"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="context"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="states"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="digits"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="icon"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="mode"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="size"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="filename"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="height"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="rowspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="align"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="selection"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="default_focus"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="filters"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="statusbar_visible"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="can_create" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="can_write" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="interval" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="avatar_field" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="write_model" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="write_field" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="filter_field" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="text" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="optional" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="add-label"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-bf"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-it"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-danger"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-info"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-muted"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-primary"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-success"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-warning"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-black"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-white"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-danger"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-info"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-muted"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-primary"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-success"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-warning"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-black"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-white"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="kanban_view_ref" /></rng:optional>
|
||||||
|
<rng:optional>
|
||||||
|
<rng:attribute name="force_save">
|
||||||
|
<rng:choice>
|
||||||
|
<rng:value>1</rng:value>
|
||||||
|
<rng:value>0</rng:value>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:attribute>
|
||||||
|
</rng:optional>
|
||||||
|
<!-- Widget *static* options defined as an arbitrary JSON dict, with
|
||||||
|
widget-dependent parameters. To be ignored if widget/client does
|
||||||
|
not support them. -->
|
||||||
|
<rng:optional><rng:attribute name="options"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="placeholder"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="data"/>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
<rng:ref name="label"/>
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="xpath"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="group"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="group">
|
||||||
|
<rng:element name="group">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:ref name="access_rights"/>
|
||||||
|
<rng:ref name="modifiable"/>
|
||||||
|
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="rowspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="expand"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="states"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="fill"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="height"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="width"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="color" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
<rng:ref name="container"/>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="button">
|
||||||
|
<rng:element name="button">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:ref name="access_rights"/>
|
||||||
|
<rng:ref name="modifiable"/>
|
||||||
|
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="disabled"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="name" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="icon" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="string" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="states" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="type" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="special" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="align" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="target"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="readonly"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="context"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="confirm"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="help"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="class"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="default_focus"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="tabindex"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="title"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="aria-label"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="aria-pressed"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="field" />
|
||||||
|
<rng:ref name="xpath" />
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="group"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="filter">
|
||||||
|
<rng:element name="filter">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:ref name="access_rights"/>
|
||||||
|
<rng:ref name="modifiable"/>
|
||||||
|
<rng:attribute name="name"/>
|
||||||
|
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="icon"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="separator" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="string" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="type" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="align" /></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="readonly"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="context"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="help"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="domain"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="date"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="default_period"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="field" />
|
||||||
|
<rng:ref name="xpath" />
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="create">
|
||||||
|
<rng:element name="create">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:attribute name="string"/>
|
||||||
|
<rng:optional><rng:attribute name="context"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="control">
|
||||||
|
<rng:element name="control">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:oneOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="create"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:oneOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
</rng:grammar>
|
||||||
45
app_common/rng/search_view.rng
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0"
|
||||||
|
xmlns:a="http://relaxng.org/ns/annotation/1.0"
|
||||||
|
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||||
|
<!-- Handling of element overloading when inheriting from a base
|
||||||
|
template
|
||||||
|
-->
|
||||||
|
<rng:include href="common.rng"/>
|
||||||
|
|
||||||
|
<rng:define name="searchpanel">
|
||||||
|
<rng:element name="searchpanel">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:optional><rng:attribute name="view_types"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="class"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="options"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:ref name="field" />
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="search">
|
||||||
|
<rng:element name="search">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
<rng:ref name="group"/>
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
<rng:optional><rng:attribute name="options"/></rng:optional>
|
||||||
|
<rng:ref name="searchpanel"/>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
<rng:start>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="search" />
|
||||||
|
</rng:choice>
|
||||||
|
</rng:start>
|
||||||
|
</rng:grammar>
|
||||||
112
app_common/rng/tree_view.rng
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0"
|
||||||
|
xmlns:a="http://relaxng.org/ns/annotation/1.0"
|
||||||
|
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||||
|
<!-- Handling of element overloading when inheriting from a base
|
||||||
|
template
|
||||||
|
-->
|
||||||
|
<rng:include href="common.rng"/>
|
||||||
|
|
||||||
|
<rng:define name="groupby">
|
||||||
|
<rng:element name="groupby">
|
||||||
|
<rng:attribute name="name"/>
|
||||||
|
<rng:optional><rng:attribute name="expand"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
<rng:define name="tree">
|
||||||
|
<rng:element name="tree">
|
||||||
|
<rng:ref name="overload"/>
|
||||||
|
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="create"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="delete"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="edit"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="multi_edit"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="multi_group_select"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="export_xlsx"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="duplicate"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="import"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="string"/></rng:optional> <!-- deprecated, has no effect anymore -->
|
||||||
|
<rng:optional><rng:attribute name="class"/></rng:optional>
|
||||||
|
<!-- Allows to take a custom View widget for handling -->
|
||||||
|
<rng:optional><rng:attribute name="js_class"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="options"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="default_order"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-bf"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-it"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-danger"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-info"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-muted"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-primary"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-success"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-warning"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-black"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="decoration-white"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-danger"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-info"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-muted"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-primary"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-success"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-warning"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-black"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="bg-white"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="banner_route"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="sample"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="action"/></rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="type"/></rng:optional>
|
||||||
|
<rng:optional>
|
||||||
|
<rng:attribute name="limit">
|
||||||
|
<rng:data type="int"/>
|
||||||
|
</rng:attribute>
|
||||||
|
</rng:optional>
|
||||||
|
<rng:optional>
|
||||||
|
<rng:attribute name="count_limit">
|
||||||
|
<rng:data type="int"/>
|
||||||
|
</rng:attribute>
|
||||||
|
</rng:optional>
|
||||||
|
<rng:optional>
|
||||||
|
<rng:attribute name="groups_limit">
|
||||||
|
<rng:data type="int"/>
|
||||||
|
</rng:attribute>
|
||||||
|
</rng:optional>
|
||||||
|
<rng:optional>
|
||||||
|
<rng:attribute name="editable">
|
||||||
|
<rng:choice>
|
||||||
|
<rng:value>top</rng:value>
|
||||||
|
<rng:value>bottom</rng:value>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:attribute>
|
||||||
|
</rng:optional>
|
||||||
|
<rng:optional><rng:attribute name="expand"/></rng:optional>
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:element name="header">
|
||||||
|
<rng:zeroOrMore>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
<rng:ref name="control"/>
|
||||||
|
<rng:ref name="field"/>
|
||||||
|
<rng:ref name="separator"/>
|
||||||
|
<rng:ref name="tree"/>
|
||||||
|
<rng:ref name="groupby"/>
|
||||||
|
<rng:ref name="button"/>
|
||||||
|
<rng:ref name="filter"/>
|
||||||
|
<rng:ref name="html"/>
|
||||||
|
<rng:element name="newline"><rng:empty/></rng:element>
|
||||||
|
</rng:choice>
|
||||||
|
</rng:zeroOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
<rng:start>
|
||||||
|
<rng:choice>
|
||||||
|
<rng:ref name="tree" />
|
||||||
|
</rng:choice>
|
||||||
|
</rng:start>
|
||||||
|
</rng:grammar>
|
||||||
BIN
app_common/static/description/banner.png
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
app_common/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
103
app_common/static/description/index.html
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced" >
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan"> </h2>
|
||||||
|
<h3 class="oe_slogan"> </h3>
|
||||||
|
<div class="oe_row">
|
||||||
|
<h3>Lastest update: v16.22.02.02</h3>
|
||||||
|
<div class="row">
|
||||||
|
<img class="oe_demo oe_screenshot img img-fluid" style="max-height: 100%;" src="banner.png">
|
||||||
|
</div>
|
||||||
|
<div class="oe_span12 oe_spaced">
|
||||||
|
<div class="alert alert-info" style="padding:8px;font-weight: 300; font-size: 20px;">
|
||||||
|
<i class="fa fa-hand-o-right"></i><b> Key features: </b>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-check-square-o text-primary"></i>
|
||||||
|
Put key function here.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-check-square-o text-primary"></i>
|
||||||
|
3. Multi-language Support.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-check-square-o text-primary"></i>
|
||||||
|
4. Multi-Company Support.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-check-square-o text-primary"></i>
|
||||||
|
5. Support Odoo 16, 15, 14, 13, 12, 11, Enterprise and Community Edition.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Setup, please run the follow command to install the lib.</h2>
|
||||||
|
<h4 class="oe_slogan"> pip install pyyaml ua-parser user-agents </h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src=".jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">So Easy to navigator and search any data.</h2>
|
||||||
|
<h4 class="oe_slogan"> </h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src=".jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Multi-language Support..</h2>
|
||||||
|
<h4 class="oe_slogan"> </h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="cnreadme.jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container oe_dark">
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan">Technical Help & Support</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 pad0">
|
||||||
|
<div class="oe_mt16">
|
||||||
|
<p><h4>
|
||||||
|
For any type of technical help & support requests, Feel free to contact us</h4></p>
|
||||||
|
<a style="background: #002e5a none repeat scroll 0% 0%; color: rgb(255, 255, 255);position: relative; overflow: hidden;"
|
||||||
|
class="btn btn-warning btn-lg" rel="nofollow" href="mailto:guohuadeng@hotmail.com"><span
|
||||||
|
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
|
||||||
|
<i class="fa fa-envelope"></i> guohuadeng@hotmail.com</a>
|
||||||
|
<p><h4>
|
||||||
|
Via QQ: 300883 (App user would not get QQ or any other IM support. Only for odoo project customize.)</h4></p>
|
||||||
|
<a style="background: #002e5a none repeat scroll 0% 0%; color: rgb(255, 255, 255);position: relative; overflow: hidden;"
|
||||||
|
class="btn btn-warning btn-lg" rel="nofollow" href="mailto:300883@qq.com"><span
|
||||||
|
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
|
||||||
|
<i class="fa fa-envelope"></i> 300883@qq.com</a>
|
||||||
|
</div>
|
||||||
|
<div class="oe_mt16">
|
||||||
|
<h4>
|
||||||
|
Visit our website for more support.</h4>
|
||||||
|
<h4>https://www.odooai.cn</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<h2>More Powerful addons, Make your odoo very easy to use, easy customize:
|
||||||
|
<a class="btn btn-primary mb16" href="http://www.odoo.com/apps/modules/browse?author=odooai.cn">odooai.cn Odoo Addons</a>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
BIN
app_common/static/img/icon_sunpop.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
app_common/static/img/logo_sunpop.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
0
app_common/static/src/js/sunpop.cn
Normal file
0
app_common/static/src/xml/sunpop.cn
Normal file
13
app_common/views/ir_cron_views.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="app_ir_cron_view_tree" model="ir.ui.view">
|
||||||
|
<field name="name">app.ir.cron.tree</field>
|
||||||
|
<field name="model">ir.cron</field>
|
||||||
|
<field name="inherit_id" ref="base.ir_cron_view_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='active']" position="before">
|
||||||
|
<field name='trigger_user_id' optional="show"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
0
app_common/wizard/sunpop.cn
Normal file
11
app_odoo_customize/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from . import wizard
|
||||||
|
from . import hooks
|
||||||
|
from .hooks import pre_init_hook
|
||||||
|
from .hooks import post_init_hook
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
198
app_odoo_customize/__manifest__.py
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 2018-11-26
|
||||||
|
# author: 欧度智能,https://www.odooai.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of odooai
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
# Odoo12在线用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/12.0/en/index.html
|
||||||
|
|
||||||
|
# Odoo12在线开发者手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/12.0/index.html
|
||||||
|
|
||||||
|
# Odoo10在线中文用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.odooai.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.odooai.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'odoo Tweak,Ai Employee,Boost,Customize All in One. Customize,UI,Boost,Security,Data,Development Enhance',
|
||||||
|
'version': '16.5.23.09.17',
|
||||||
|
'author': 'odooai.cn',
|
||||||
|
'category': 'Extra Tools',
|
||||||
|
'website': 'https://www.odooai.cn',
|
||||||
|
'live_test_url': 'https://demo.odooapp.cn',
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'sequence': 2,
|
||||||
|
'images': ['static/description/banner.gif'],
|
||||||
|
'summary': """
|
||||||
|
Ai as employee. 1 click Tweak odoo. 48 Functions odoo enhancement. for Customize, UI, Boost, Security, Development.
|
||||||
|
Easy reset data, clear data, reset account chart, reset Demo data.
|
||||||
|
For quick debug. Set brand, Language Switcher all in one.
|
||||||
|
""",
|
||||||
|
'depends': [
|
||||||
|
'app_common',
|
||||||
|
'base_setup',
|
||||||
|
'web',
|
||||||
|
'mail',
|
||||||
|
# 'digest',
|
||||||
|
# when enterprise
|
||||||
|
# 'web_mobile'
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'security/res_groups.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/app_odoo_customize_views.xml',
|
||||||
|
'views/app_theme_config_settings_views.xml',
|
||||||
|
'views/res_config_settings_views.xml',
|
||||||
|
'views/ir_views.xml',
|
||||||
|
'views/ir_module_module_views.xml',
|
||||||
|
'views/ir_translation_views.xml',
|
||||||
|
'views/ir_module_addons_path_views.xml',
|
||||||
|
'views/ir_ui_menu_views.xml',
|
||||||
|
'views/ir_ui_view_views.xml',
|
||||||
|
'views/ir_model_fields_views.xml',
|
||||||
|
'views/ir_model_data_views.xml',
|
||||||
|
# data
|
||||||
|
'data/ir_config_parameter_data.xml',
|
||||||
|
'data/ir_module_module_data.xml',
|
||||||
|
# 'data/digest_template_data.xml',
|
||||||
|
'data/res_company_data.xml',
|
||||||
|
'data/res_config_settings_data.xml',
|
||||||
|
],
|
||||||
|
# 'qweb': [
|
||||||
|
# 'static/src/xml/*.xml',
|
||||||
|
# ],
|
||||||
|
'assets': {
|
||||||
|
'web.assets_backend': [
|
||||||
|
'app_odoo_customize/static/src/scss/app.scss',
|
||||||
|
'app_odoo_customize/static/src/scss/ribbon.scss',
|
||||||
|
'app_odoo_customize/static/src/scss/dialog.scss',
|
||||||
|
'app_odoo_customize/static/src/js/user_menu.js',
|
||||||
|
'app_odoo_customize/static/src/js/ribbon.js',
|
||||||
|
'app_odoo_customize/static/src/js/dialog.js',
|
||||||
|
'app_odoo_customize/static/src/webclient/*.js',
|
||||||
|
'app_odoo_customize/static/src/webclient/*.xml',
|
||||||
|
'app_odoo_customize/static/src/xml/res_config_edition.xml',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'pre_init_hook': 'pre_init_hook',
|
||||||
|
'post_init_hook': 'post_init_hook',
|
||||||
|
'installable': True,
|
||||||
|
'application': True,
|
||||||
|
'auto_install': True,
|
||||||
|
'description': """
|
||||||
|
|
||||||
|
App Customize Odoo (Change Title,Language,Documentation,Quick Debug)
|
||||||
|
============
|
||||||
|
White label odoo.
|
||||||
|
Support odoo 16,15,14,13,12,11,10,9.
|
||||||
|
You can config odoo, make it look like your own platform.
|
||||||
|
1. Deletes Odoo label in footer
|
||||||
|
2. Replaces "Odoo" in Windows title
|
||||||
|
3. Customize Documentation, Support, About links and title in usermenu
|
||||||
|
4. Adds "Developer mode" link to the top right-hand User Menu.
|
||||||
|
5. Adds Quick Language Switcher to the top right-hand User Menu.
|
||||||
|
6. Adds Country flags to the top right-hand User Menu.
|
||||||
|
7. Adds English and Chinese user documentation access to the top right-hand User Menu.
|
||||||
|
8. Adds developer documentation access to the top right-hand User Menu.
|
||||||
|
9. Customize "My odoo.com account" button
|
||||||
|
10. Standalone setting panel, easy to setup.
|
||||||
|
11. Provide 236 country flags.
|
||||||
|
12. Multi-language Support.
|
||||||
|
13. Change Powered by Odoo in login screen.(Please change '../views/app_odoo_customize_view.xml' #15)
|
||||||
|
14. Quick delete test data in Apps: Sales/POS/Purchase/MRP/Inventory/Accounting/Project/Message/Workflow etc.
|
||||||
|
15. Reset All the Sequence to beginning of 1: SO/PO/MO/Invoice...
|
||||||
|
16. Fix odoo reload module translation bug while enable english language
|
||||||
|
17. Stop Odoo Auto Subscribe(Moved to app_odoo_boost)
|
||||||
|
18. Show/Hide Author and Website in Apps Dashboard
|
||||||
|
19. One Click to clear all data (Sometime pls click twice)
|
||||||
|
20. Show quick upgrade in app dashboard, click to show module info not go to odoo.com
|
||||||
|
21. Can clear and reset account chart. Be cautious
|
||||||
|
22. Update online manual and developer document to odoo12
|
||||||
|
23. Add reset or clear website blog data
|
||||||
|
24. Customize Odoo Native Module(eg. Enterprise) Url
|
||||||
|
25. Add remove expense data
|
||||||
|
26. Add multi uninstall modules
|
||||||
|
27. Add odoo boost modules link.
|
||||||
|
28. Easy Menu manager.
|
||||||
|
29. Apps version compare. Add Install version in App list. Add Local updatable filter in app list.
|
||||||
|
30. 1 key export app translate file like .po file.
|
||||||
|
31. Show or hide odoo Referral in the top menu.
|
||||||
|
32. Fix odoo bug of complete name bug of product category and stock location..
|
||||||
|
33. Add Demo Ribbon Setting.
|
||||||
|
34. Add Remove all quality data.
|
||||||
|
35. Fixed for odoo 14.
|
||||||
|
36. Add refresh translate for multi module.
|
||||||
|
37. Easy noupdate manage for External Identifiers(xml_id)
|
||||||
|
38. Add Draggable and sizeable Dialog enable.
|
||||||
|
39. Only erp manager can see debug menu..
|
||||||
|
40. Fix support for enterprise version.
|
||||||
|
41. Fix odoo bug, when click Preferences menu not hide in mobile.
|
||||||
|
42. Mobile Enhance. 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.
|
||||||
|
46. Add Help documentation anywhere. easy get help for any odoo operation or action.
|
||||||
|
47. Add ai robot app integration. Use Ai as your employee.
|
||||||
|
|
||||||
|
This module can help to white label the Odoo.
|
||||||
|
Also helpful for training and support for your odoo end-user.
|
||||||
|
The user can get the help document just by one click.
|
||||||
|
## 在符合odoo开源协议的前提下,自定义你的odoo系统
|
||||||
|
可完全自行设置下列选项,将 odoo 整合进自有软件产品
|
||||||
|
支持odoo 16,15,14,13,12, 11, 10, 9 版本,社区版企业版通用
|
||||||
|
1. 删除菜单导航页脚的 Odoo 标签
|
||||||
|
2. 将弹出窗口中 "Odoo" 设置为自定义名称
|
||||||
|
3. 自定义用户菜单中的 Documentation, Support, About 的链接
|
||||||
|
4. 在用户菜单中增加快速切换开发模式
|
||||||
|
5. 在用户菜单中增加快速切换多国语言
|
||||||
|
6. 对语言菜单进行美化,设置国旗图标
|
||||||
|
7. 在用户菜单中增加中/英文用户手册,可以不用翻墙加速了
|
||||||
|
8. 在用户菜单中增加开发者手册,含python教程,jquery参考,Jinja2模板,PostgresSQL参考
|
||||||
|
9. 在用户菜单中自定义"My odoo.com account"
|
||||||
|
10. 单独设置面板,每个选项都可以自定义
|
||||||
|
11. 提供236个国家的国旗文件(部份需要自行设置文件名)
|
||||||
|
12. 多语言版本
|
||||||
|
13. 自定义登陆界面中的 Powered by Odoo
|
||||||
|
14. 快速删除测试数据,支持模块包括:销售/POS门店/采购/生产/库存/会计/项目/消息与工作流等.
|
||||||
|
15. 将各类单据的序号重置,从1开始,包括:SO/PO/MO/Invoice 等
|
||||||
|
16. 修复odoo启用英文后模块不显示中文的Bug
|
||||||
|
17. 可停用odoo自动订阅功能,避免“同样对象关注2次”bug,同时提升性能
|
||||||
|
18. 显示/隐藏应用的作者和网站-在应用安装面板中
|
||||||
|
19. 一键清除所有数据(视当前数据情况,有时需点击2次)
|
||||||
|
20. 在应用面板显示快速升级按键,点击时不会导航至 odoo.com
|
||||||
|
21. 清除并重置会计科目表
|
||||||
|
22. 全新升级将odoo12用户及开发手册导航至国内网站,或者自己定义的网站
|
||||||
|
23. 增加清除网站数据功能
|
||||||
|
24. 自定义 odoo 原生模块跳转的url(比如企业版模块)
|
||||||
|
25. 增加删除费用报销数据功能
|
||||||
|
26. 增加批量卸载模块功能
|
||||||
|
27. 增加odoo加速功能
|
||||||
|
28. 快速管理顶级菜单
|
||||||
|
29. App版本比较,快速查看可本地更新的模块
|
||||||
|
30. 一键导出翻译文件 po
|
||||||
|
31. 显示或去除 odoo 推荐
|
||||||
|
32. 增加修复品类及区位名的操作
|
||||||
|
33. 增加 Demo 的显示设置
|
||||||
|
34. 增加清除质检数据
|
||||||
|
35. 优化至odoo14适用
|
||||||
|
36. 可为多个模块强制更新翻译
|
||||||
|
37. noupdate字段的快速管理,主要针对 xml_id
|
||||||
|
38. 对话框可拖拽,可缩放,自动大屏优化
|
||||||
|
39. 只有系统管理员可以操作快速debug
|
||||||
|
40. 增强对企业版的支持
|
||||||
|
41. 修正odoo原生移动端菜单bug,点击个人设置时,原菜单不隐藏等
|
||||||
|
42. 可设置导航栏在上方还是下方,分开桌面与移动端.
|
||||||
|
43. 可设置只允许管理员进入开发者模式,不可在url中直接debut=1来调试
|
||||||
|
44. 可配置停用自动用户订阅功能,这会提速odoo,减少资源消耗
|
||||||
|
45. 为应用模块增加模块路径信息
|
||||||
|
46. 增加快速帮助文档,可以在任意操作中获取相关的 odoo 帮助.
|
||||||
|
""",
|
||||||
|
}
|
||||||
4
app_odoo_customize/controllers/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# todo: website 有bug oauth
|
||||||
|
from . import controllers
|
||||||
26
app_odoo_customize/controllers/controllers.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# -*- 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 AppHome(Home):
|
||||||
|
|
||||||
|
@http.route()
|
||||||
|
def web_client(self, s_action=None, **kw):
|
||||||
|
# todo: 当前只对 web,要调整为也对 website
|
||||||
|
res = super(AppHome, self).web_client(s_action, **kw)
|
||||||
|
|
||||||
|
if kw.get('debug', False):
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
156
app_odoo_customize/data/digest_template_data.xml
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="digest.digest_mail_template" model="mail.template">
|
||||||
|
<field name="body_html"><![CDATA[
|
||||||
|
<table style="width: 100%; border-spacing: 0; font-family: Helvetica,Arial,Verdana,sans-serif;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top" style="border-collapse: collapse; padding: 0">
|
||||||
|
% set user = ctx.get('user', user)
|
||||||
|
% set company = user.company_id
|
||||||
|
% set data = object.compute_kpis(company, user)
|
||||||
|
% set tips = object.compute_tips(company, user)
|
||||||
|
% set kpi_actions = object.compute_kpis_actions(company, user)
|
||||||
|
% set kpis = data.yesterday.keys()
|
||||||
|
<table style="width: 100%; max-width: 600px; border-spacing: 0; border: 1px solid #e7e7e7; border-bottom: none; color: #6e7172; line-height: 23px; text-align: left;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; padding: 10px 40px; text-align: left;">
|
||||||
|
<strong style="margin-left: -22px; color: #000000; font-size: 22px; line-height: 32px;">${company.name} at a glance</strong>
|
||||||
|
<div style="color: #000000; font-size: 15px; margin-left:-22px;">${datetime.date.today().strftime('%B %d, %Y')}</div>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right; padding: 10px 40px">
|
||||||
|
<img style="padding: 0px; margin: 0px; height: auto; width: 80px;" src="/logo.png?company=${company.id}"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr><td colspan="2" style="text-align: center;">
|
||||||
|
<hr width="95%" style="background-color: rgb(204,204,204); border: medium none; clear: both; display: block; font-size: 0px; min-height: 1px; line-height: 0; margin: 16px 0px 16px 14px;"/>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
% for kpi in kpis:
|
||||||
|
<table style="border-spacing: 0; width: 100%; max-width: 600px;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; background-color: #ffffff; border-left: 1px solid #e7e7e7; border-right: 1px solid #e7e7e7; line-height: 21px; padding: 0 20px 10px 20px; text-align: left;"><br/>
|
||||||
|
<span style="color: #3d466e; font-size: 18px; font-weight: 500; line-height: 23px;">${object.fields_get()[kpi]['string']}</span>
|
||||||
|
%if kpi in kpi_actions:
|
||||||
|
<span style="float: right;">
|
||||||
|
<a href="/web#action=${kpi_actions[kpi]}">View more</a>
|
||||||
|
</span>
|
||||||
|
%endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; margin: 0; padding:0;">
|
||||||
|
<table style="width: 100%; border-spacing: 0; background-color: #f9f9f9; border: 1px solid #e7e7e7; border-top: none;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; margin: 0; padding: 0; display: block; border-top: 2px solid #56b3b5;">
|
||||||
|
<table style="width: 100%; max-width: 199px; border-spacing: 0;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; padding: 20px; text-align: center;">
|
||||||
|
<span style="color: #56b3b5; font-size: 35px; font-weight: bold; text-decoration: none; line-height: 36px;">${data['yesterday'][kpi][kpi]}</span><br/>
|
||||||
|
<span style="color: #888888; display: inline-block; font-size: 12px; line-height: 18px; text-transform: uppercase;">Yesterday</span>
|
||||||
|
% if data['yesterday'][kpi]['margin'] != 0.0:
|
||||||
|
<span style="color: #888888; display: block; font-size: 12px; line-height: 18px; text-transform: uppercase;">
|
||||||
|
% if data['yesterday'][kpi]['margin'] > 0.0:
|
||||||
|
<span style="color: #0bbc22;">▲</span>${"%.2f" % data['yesterday'][kpi]['margin']} %
|
||||||
|
% endif
|
||||||
|
% if data['yesterday'][kpi]['margin'] < 0.0:
|
||||||
|
<span style="color: #ff0000;">▼</span>${"%.2f" % data['yesterday'][kpi]['margin']} %
|
||||||
|
% endif
|
||||||
|
</span>
|
||||||
|
% endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td style="border-collapse: collapse; margin: 0; padding: 0; border-top: 2px solid #9a5b82;">
|
||||||
|
<table style="width: 100%; max-width: 199px; border-spacing: 0; margin: 0; padding: 0;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; padding: 20px; text-align: center;">
|
||||||
|
<span style="color: #9a5b82; font-size: 35px; font-weight: bold; text-decoration: none; line-height: 36px;">${data['lastweek'][kpi][kpi]}</span><br/>
|
||||||
|
<span style="color: #888888; display: inline-block; font-size: 12px; line-height: 18px; text-transform: uppercase;">Last 7 Days</span>
|
||||||
|
% if data['lastweek'][kpi]['margin'] != 0.0:
|
||||||
|
<span style="color: #888888; display: block; font-size: 12px; line-height: 18px; text-transform: uppercase;">
|
||||||
|
% if data['lastweek'][kpi]['margin'] > 0.0:
|
||||||
|
<span style="color: #0bbc22;">▲</span>${"%.2f" % data['lastweek'][kpi]['margin']} %
|
||||||
|
% endif
|
||||||
|
% if data['lastweek'][kpi]['margin'] < 0.0:
|
||||||
|
<span style="color: #ff0000;">▼</span>${"%.2f" % data['lastweek'][kpi]['margin']} %
|
||||||
|
%endif
|
||||||
|
</span>
|
||||||
|
%endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td style="border-collapse: collapse; margin: 0; padding: 0; border-top: 2px solid #56b3b5;">
|
||||||
|
<table style="width: 100%; max-width: 199px; border-spacing: 0; margin: 0; padding: 0;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; margin: 0; padding: 20; text-align: center;">
|
||||||
|
<span style="color: #56b3b5; font-size: 35px; font-weight: bold; text-decoration: none; line-height: 36px">${data['lastmonth'][kpi][kpi]}</span><br/>
|
||||||
|
<span style="color: #888888; display: inline-block; font-size: 12px; line-height: 18px; text-transform: uppercase;">Last 30 Days</span>
|
||||||
|
% if data['lastmonth'][kpi]['margin'] != 0.0:
|
||||||
|
<span style="color: #888888; display: block; font-size: 12px; line-height: 18px; text-transform: uppercase;">
|
||||||
|
% if data['lastmonth'][kpi]['margin'] > 0.0:
|
||||||
|
<span style="color: #0bbc22;">▲</span>${"%.2f" % data['lastmonth'][kpi]['margin']} %
|
||||||
|
% endif
|
||||||
|
% if data['lastmonth'][kpi]['margin'] < 0.0:
|
||||||
|
<span style="color: #ff0000;">▼</span>${"%.2f" % data['lastmonth'][kpi]['margin']} %
|
||||||
|
%endif
|
||||||
|
</span>
|
||||||
|
%endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
% endfor
|
||||||
|
% if tips:
|
||||||
|
<table style="width: 100%; max-width: 600px; margin-top: 5px; border: 1px solid #e7e7e7;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; background-color: #ffffff; line-height: 21px; padding: 0px 20px;"><br/>
|
||||||
|
<div style="color: #3d466e; line-height: 23px;">${ctx['tip_description']|safe}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
% endif
|
||||||
|
<table style="width: 100%; max-width: 600px; margin-top: 5px; border: 1px solid #e7e7e7;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; background-color: #ffffff; line-height: 21px; padding: 0 20px 10px 20px; text-align: center;"><br/>
|
||||||
|
<div style="color: #3d466e; font-size: 16px; font-weight: 600; line-height: 23px;">Run your business from anywhere with Odoo Mobile.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div style="text-align: center;"><a href="https://www.odooai.cn" target="_blank"><img src="/digest/static/src/img/google_play.png" style="display: inline-block; height: 30px; margin-left: auto; margin-right: 12px;"/></a><a href="https://www.odooai.cn" target="_blank"><img src="/digest/static/src/img/app_store.png" style="display: inline-block; height: 30px; margin-left: 12px; margin-right: auto;"/></a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<table style="margin-top: 5px; border: 1px solid #e7e7e7; font-size: 15px; width: 100%; max-width: 600px;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-collapse: collapse; margin: 0; padding: 10px 20px;">
|
||||||
|
% if user.has_group('base.group_system'):
|
||||||
|
<div style="margin-top: 20px;">
|
||||||
|
Want to customize the email?
|
||||||
|
<a href="/web#view_type=form&model=digest.digest&id=${object.id}" target="_blank" style="color: #875A7B;">Choose the metrics you care about</a>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
% endif
|
||||||
|
<p style="font-size: 11px; margin-top: 10px;">
|
||||||
|
<strong>
|
||||||
|
Sent by
|
||||||
|
<a href="https://www.odoo.com" style="text-decoration: none; color: #875A7B;">Odoo</a> - <a href="/web#view_type=form&model=digest.digest&id=${object.id}" target="_blank" style="color: #888888;">Unsubscribe</a>
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
]]></field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
27
app_odoo_customize/data/ir_config_parameter_data.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<openerp>
|
||||||
|
<data noupdate="1">
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_system_name', 'odooAi')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_lang', 'True')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_debug', 'True')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_documentation', 'True')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_documentation_dev', 'True')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_support', 'True')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_account', 'True')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_enterprise', 'False')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_share', 'False')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_show_poweredby', 'False')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_documentation_url', 'https://www.odooai.cn/documentation/16.0/index.html')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_documentation_dev_url', 'https://www.odooai.cn/documentation/16.0/developer.html')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_support_url', 'https://www.odooai.cn/trial')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_account_title', 'My Online Account')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_account_url', 'https://www.odooai.cn/my')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_enterprise_url', 'https://www.odooai.cn')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_ribbon_name', 'odooai.cn')"/>
|
||||||
|
<function model="ir.config_parameter" name="set_param" eval="('app_ribbon_color', '#f0f0f0')"/>
|
||||||
|
<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>
|
||||||
64
app_odoo_customize/data/ir_module_module_data.xml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record model="ir.module.module" id="base.module_web_studio">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_timesheet_grid">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_account_accountant">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_helpdesk">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_hr_appraisal">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_marketing_automation">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_mrp_plm">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_quality_control">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_sale_ebay">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_sale_subscription">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_sign">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_stock_barcode">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_voip">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_mrp_workorder">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.module.module" id="base.module_web_mobile">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
9
app_odoo_customize/data/res_company_data.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!-- 创建数据库时首个公司信息 -->
|
||||||
|
<record id="base.main_company" model="res.company">
|
||||||
|
<field name="website">https://www.odooai.cn</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
6
app_odoo_customize/data/res_config_settings_data.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<function model="res.config.settings" name="set_module_url"/>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
49
app_odoo_customize/hooks.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 2018-10-12
|
||||||
|
# author: 欧度智能,https://www.odooai.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of odooai
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||||
|
|
||||||
|
# Odoo在线中文用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.odooai.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.odooai.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID, _
|
||||||
|
|
||||||
|
|
||||||
|
def pre_init_hook(cr):
|
||||||
|
try:
|
||||||
|
# 更新企业版指向
|
||||||
|
sql = "UPDATE ir_module_module SET website = '%s' WHERE license like '%s' and website <> ''" % ('https://www.odooai.cn', 'OEEL%')
|
||||||
|
cr.execute(sql)
|
||||||
|
cr.commit()
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def post_init_hook(cr, registry):
|
||||||
|
# a = check_module_installed(cr, ['app_web_superbar','aaaaa'])
|
||||||
|
pass
|
||||||
|
# cr.execute("")
|
||||||
|
|
||||||
|
def uninstall_hook(cr, registry):
|
||||||
|
"""
|
||||||
|
数据初始化,卸载时执行
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def check_module_installed(cr, modules):
|
||||||
|
# modules 输入参数是个 list,如 ['base', 'sale']
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
installed = False
|
||||||
|
m = env['ir.module.module'].sudo().search([('name', 'in', modules), ('state', 'in', ['installed', 'to install', 'to upgrade'])])
|
||||||
|
if len(m) == len(modules):
|
||||||
|
installed = True
|
||||||
|
return len(m)
|
||||||
|
|
||||||
772
app_odoo_customize/i18n/zh_CN.po
Normal file
@@ -0,0 +1,772 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * app_odoo_customize
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 16.0+e-20230721\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2023-08-24 11:23+0000\n"
|
||||||
|
"PO-Revision-Date: 2023-08-24 11:23+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid ""
|
||||||
|
"<span class=\"col-3 col-lg-2 text-left\">\n"
|
||||||
|
"\t\t\t\t\t\t\t\t\tAccounting\n"
|
||||||
|
"\t\t\t\t\t\t\t\t</span>"
|
||||||
|
msgstr ""
|
||||||
|
"<span class=\"col-3 col-lg-2 text-left\">\n"
|
||||||
|
" 财务\n"
|
||||||
|
" </span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">All Business</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">所有业务数据</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Base Models</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">基础数据</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Expense</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">费用</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Inventory</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">库存</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">MRP</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">制造</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">POS</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">POS收银</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Project</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">项目</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Purchase</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">采购</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Quality</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">质检</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Sale</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">销售</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span class=\"col-3 col-lg-2 text-left\">Website And Blog</span>"
|
||||||
|
msgstr "<span class=\"col-3 col-lg-2 text-left\">网站与博客</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.ir_module_addons_path_form_view
|
||||||
|
msgid "<span class=\"o_stat_text\"> Modules </span>"
|
||||||
|
msgstr "<span class=\"o_stat_text\"> 模块 </span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "<span>Set to False to hide</span>"
|
||||||
|
msgstr "<span>设置为 False 则不显示</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/js/user_menu.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Activate Assets Debugging"
|
||||||
|
msgstr "激活开发者模式 (assets)"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/js/user_menu.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Activate the developer mode"
|
||||||
|
msgstr "激活开发者模式"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_module__addons_path
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_view_module_filter
|
||||||
|
msgid "Addons Path"
|
||||||
|
msgstr "模块所在目录"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_module__addons_path_id
|
||||||
|
msgid "Addons Path ID"
|
||||||
|
msgstr "模块路径"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.actions.act_window,name:app_odoo_customize.action_ir_module_addons_path
|
||||||
|
#: model:ir.ui.menu,name:app_odoo_customize.menu_ir_module_addons_path
|
||||||
|
msgid "Addons Paths"
|
||||||
|
msgstr "模块路径"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__module_app_chatgpt
|
||||||
|
msgid "Ai Center"
|
||||||
|
msgstr "Ai服务中心"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields.selection,name:app_odoo_customize.selection__res_config_settings__app_navbar_pos_mobile__bottom
|
||||||
|
#: model:ir.model.fields.selection,name:app_odoo_customize.selection__res_config_settings__app_navbar_pos_pc__bottom
|
||||||
|
msgid "Bottom"
|
||||||
|
msgstr "底部"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_debug_only_admin
|
||||||
|
msgid ""
|
||||||
|
"Check to only Debug / Debug Assets for Odoo Admin. Deny debug from url for "
|
||||||
|
"other user."
|
||||||
|
msgstr "勾选后仅Odoo管理员可操作开发者模式。 拒绝其他用户从URL进入调试模式。"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_stop_subscribe
|
||||||
|
msgid "Check to stop subscribe and follow. This to make odoo speed up."
|
||||||
|
msgstr "选中以停止订阅并关注。这是为了让odoo加速。"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Clean and reset Account Chart"
|
||||||
|
msgstr "清除会计科目,便于重置"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Click to set"
|
||||||
|
msgstr "点击设置"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__color
|
||||||
|
msgid "Color"
|
||||||
|
msgstr "颜色"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_res_config_settings
|
||||||
|
msgid "Config Settings"
|
||||||
|
msgstr "配置设置"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.replace_copyright_name
|
||||||
|
msgid "Copyright &copy;"
|
||||||
|
msgstr "版权所有 &copy;"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__create_uid
|
||||||
|
msgid "Created by"
|
||||||
|
msgstr "创建者"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__create_date
|
||||||
|
msgid "Created on"
|
||||||
|
msgstr "创建于"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_enterprise_url
|
||||||
|
msgid "Customize Module Url(eg. Enterprise)"
|
||||||
|
msgstr "自定义模块链接(如企业版)"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Data Cleaning (Be careful to do that!)"
|
||||||
|
msgstr "数据清理(请谨慎操作!)"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_debug_only_admin
|
||||||
|
msgid "Debug for Admin"
|
||||||
|
msgstr "仅系统管理员可调试"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All BOM"
|
||||||
|
msgstr "删除所有物料清单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Expense and Sheet"
|
||||||
|
msgstr "删除所有费用与报销申请"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid ""
|
||||||
|
"Delete All MRP/Sale/Purchase/Account/MRP/Inventory/Project/Message/Workflow"
|
||||||
|
msgstr ""
|
||||||
|
"清除所有业务数据 MRP/Sale/Purchase/Account/MRP/Inventory/Project/Message/Workflow"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Manufacturing Order"
|
||||||
|
msgstr "删除所有生产单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Message"
|
||||||
|
msgstr "删除所有消息"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Move/Picking/Package/Lot"
|
||||||
|
msgstr "删除所有库存调拨/拣货/包装/批次数据"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All POS Order"
|
||||||
|
msgstr "删除所有POS订单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Product"
|
||||||
|
msgstr "删除所有产品及变体"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Product Attribute"
|
||||||
|
msgstr "删除所有产品属性"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Project/Task/Forecast"
|
||||||
|
msgstr "删除所有项目/任务/预测"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Purchase Order and Requisition"
|
||||||
|
msgstr "删除所有询价单、采购单,采购招标"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Quality"
|
||||||
|
msgstr "删除所有质检单据"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Quality Setting"
|
||||||
|
msgstr "删除所有质检设置"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Sales Order"
|
||||||
|
msgstr "删除所有报价单、销售单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Voucher/Invoice/Bill"
|
||||||
|
msgstr "删除所有收据/发票/账单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Website/Blog"
|
||||||
|
msgstr "删除所有网站/博客"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Delete All Workflow"
|
||||||
|
msgstr "删除所有工作流"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_documentation_dev_url
|
||||||
|
msgid "Developer Documentation Url"
|
||||||
|
msgstr "开发者手册链接"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__display_name
|
||||||
|
msgid "Display Name"
|
||||||
|
msgstr "显示名称"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/js/user_menu.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Documentation"
|
||||||
|
msgstr "支持文档"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_documentation_url
|
||||||
|
msgid "Documentation Url"
|
||||||
|
msgstr "用户手册链接"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/xml/res_config_edition.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Edition)"
|
||||||
|
msgstr "版)"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_mail_thread
|
||||||
|
msgid "Email Thread"
|
||||||
|
msgstr "邮件会话"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.actions.act_window,name:app_odoo_customize.action_server_module_multi_get_po
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_module_view_kanban
|
||||||
|
msgid "Export Translation"
|
||||||
|
msgstr "导出翻译"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Extra Feature"
|
||||||
|
msgstr "增强功能"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__module_app_odoo_doc
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Get Help Documentation on current odoo operation or topic."
|
||||||
|
msgstr "获取有关当前 odoo 操作或主题的帮助文档。"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid ""
|
||||||
|
"Get Help Documentation on current odoo operation or topic. Click to get"
|
||||||
|
msgstr "获取有关odoo当前操作或主题的帮助文档。点击获取"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_ir_http
|
||||||
|
msgid "HTTP Routing"
|
||||||
|
msgstr "HTTP 路由"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__module_app_odoo_doc
|
||||||
|
msgid "Help Document Anywhere"
|
||||||
|
msgstr "即时查看帮助"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__id
|
||||||
|
msgid "ID"
|
||||||
|
msgstr "ID"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_base_language_install
|
||||||
|
msgid "Install Language"
|
||||||
|
msgstr "安装语言"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path____last_update
|
||||||
|
msgid "Last Modified on"
|
||||||
|
msgstr "上次修改时间"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__write_uid
|
||||||
|
msgid "Last Updated by"
|
||||||
|
msgstr "最后更新者"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__write_date
|
||||||
|
msgid "Last Updated on"
|
||||||
|
msgstr "最后更新于"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/js/user_menu.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Leave the Developer Tools"
|
||||||
|
msgstr "离开开发者模式"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_module__license
|
||||||
|
msgid "License"
|
||||||
|
msgstr "许可协议"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.ui.menu,name:app_odoo_customize.menu_app_demo_data
|
||||||
|
msgid "Load demo data"
|
||||||
|
msgstr "加载演示数据"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_module__local_updatable
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_view_module_filter
|
||||||
|
msgid "Local updatable"
|
||||||
|
msgstr "可本地更新"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_ir_module_module
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__module_ids
|
||||||
|
msgid "Module"
|
||||||
|
msgstr "模块"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_ir_module_addons_path
|
||||||
|
msgid "Module Addons Path"
|
||||||
|
msgstr "模块所在目录"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__module_count
|
||||||
|
msgid "Module Count"
|
||||||
|
msgstr "模块计数"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.ir_module_addons_path_kanban_view
|
||||||
|
msgid "Modules"
|
||||||
|
msgstr "模块"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_account_title
|
||||||
|
msgid "My Odoo.com Account Title"
|
||||||
|
msgstr "我的帐户显示标题"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_account_url
|
||||||
|
msgid "My Odoo.com Account Url"
|
||||||
|
msgstr "我的帐户链接"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_navbar_pos_mobile
|
||||||
|
msgid "Navbar Mobile"
|
||||||
|
msgstr "导航栏移动"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_navbar_pos_pc
|
||||||
|
msgid "Navbar PC"
|
||||||
|
msgstr "导航栏电脑"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Navigator"
|
||||||
|
msgstr "导航栏"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_view_model_data_search
|
||||||
|
msgid "No Updatable"
|
||||||
|
msgstr "无需更新"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "OEM & Boost"
|
||||||
|
msgstr "定制与增强"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/xml/res_config_edition.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Odoo"
|
||||||
|
msgstr "Odoo欧度"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__path
|
||||||
|
msgid "Path"
|
||||||
|
msgstr "路径"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__path_temp
|
||||||
|
msgid "Path Temp"
|
||||||
|
msgstr "路径Temp"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Please confirm to delete the select data?"
|
||||||
|
msgstr "您确认要删除指定数据?"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.replace_login
|
||||||
|
msgid ""
|
||||||
|
"Powered by\n"
|
||||||
|
" <span>odooai.cn</span>"
|
||||||
|
msgstr "技术支持 <span>odooai.cn</span>"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/js/user_menu.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Preferences"
|
||||||
|
msgstr "偏好"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/js/user_menu.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Refresh Page"
|
||||||
|
msgstr "刷新本页"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.actions.server,name:app_odoo_customize.action_server_module_multi_refresh_po
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_module_view_kanban
|
||||||
|
msgid "Refresh Translation"
|
||||||
|
msgstr "刷新翻译"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Reset Category And Location Complete Name"
|
||||||
|
msgstr "重置品类及区位显示名"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.ui.menu,name:app_odoo_customize.menu_ir_cron
|
||||||
|
msgid "Scheduled Actions"
|
||||||
|
msgstr "计划任务"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Security and Boost"
|
||||||
|
msgstr "安全与提速"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.actions.act_window,name:app_odoo_customize.action_app_theme_config
|
||||||
|
#: model:ir.ui.menu,name:app_odoo_customize.menu_app_theme_config
|
||||||
|
msgid "OEM and Boost"
|
||||||
|
msgstr "odoo定制OEM及增强"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_system_name
|
||||||
|
msgid "Setup System Name,which replace Odoo"
|
||||||
|
msgstr "设定系统名称,代替原 Odoo 字样"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_ir_module_addons_path__name
|
||||||
|
msgid "Short Name"
|
||||||
|
msgstr "简称"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__group_show_author_in_apps
|
||||||
|
#: model:res.groups,name:app_odoo_customize.group_show_author_in_apps
|
||||||
|
msgid "Show Author in Apps Dashboard"
|
||||||
|
msgstr "显示应用的作者-在安装面板中"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_ribbon_name
|
||||||
|
msgid "Show Demo Ribbon"
|
||||||
|
msgstr "显示全站测试标签"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_documentation_dev
|
||||||
|
msgid "Show Developer Documentation"
|
||||||
|
msgstr "显示开发者文档"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_documentation
|
||||||
|
msgid "Show Documentation"
|
||||||
|
msgstr "显示文档"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_enterprise
|
||||||
|
msgid "Show Enterprise Tag"
|
||||||
|
msgstr "显示升级企业版标签提醒"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_account
|
||||||
|
msgid "Show My Account"
|
||||||
|
msgstr "显示我的帐户"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__module_odoo_referral
|
||||||
|
msgid "Show Odoo Referral"
|
||||||
|
msgstr "显示Odoo推荐"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_poweredby
|
||||||
|
msgid "Show Powered by Odoo"
|
||||||
|
msgstr "显示Powered by Odoo"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_debug
|
||||||
|
msgid "Show Quick Debug"
|
||||||
|
msgstr "显示快速调试"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_lang
|
||||||
|
msgid "Show Quick Language Switcher"
|
||||||
|
msgstr "显示快速多语言切换"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:res.groups,name:app_odoo_customize.group_show_quick_upgrade
|
||||||
|
msgid "Show Quick Upgrade in Apps Dashboard"
|
||||||
|
msgstr "在应用面板显示快速升级按键"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_share
|
||||||
|
msgid "Show Share Dashboard"
|
||||||
|
msgstr "显示分享Odoo"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_show_support
|
||||||
|
msgid "Show Support"
|
||||||
|
msgstr "显示支持"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_stop_subscribe
|
||||||
|
msgid "Stop Odoo Subscribe"
|
||||||
|
msgstr "停止Odoo订阅"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/js/user_menu.js:0
|
||||||
|
#, python-format
|
||||||
|
msgid "Support"
|
||||||
|
msgstr "技术支持"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_support_url
|
||||||
|
msgid "Support Url"
|
||||||
|
msgstr "支持链接"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,field_description:app_odoo_customize.field_res_config_settings__app_system_name
|
||||||
|
msgid "System Name"
|
||||||
|
msgstr "系统名称"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.ui.menu,name:app_odoo_customize.menu_ir_config_list
|
||||||
|
msgid "System Parameters"
|
||||||
|
msgstr "系统参数"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-python
|
||||||
|
#: code:addons/app_odoo_customize/models/ir_module_module.py:0
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"The languages that you selected have been successfully update."
|
||||||
|
" You still need to Upgrade the apps to make it "
|
||||||
|
"worked."
|
||||||
|
msgstr "你选择的翻译语言已更新。但你仍然需要更新相关模块才可生效。"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_edit_menu_access_search
|
||||||
|
msgid "Top Menu"
|
||||||
|
msgstr "顶级菜单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields.selection,name:app_odoo_customize.selection__res_config_settings__app_navbar_pos_mobile__top
|
||||||
|
#: model:ir.model.fields.selection,name:app_odoo_customize.selection__res_config_settings__app_navbar_pos_pc__top
|
||||||
|
msgid "Top(Default)"
|
||||||
|
msgstr "顶部(默认)"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "UI Config"
|
||||||
|
msgstr "用户界面配置"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__group_show_author_in_apps
|
||||||
|
msgid "Uncheck to Hide Author and Website in Apps Dashboard"
|
||||||
|
msgstr "不选中则会隐藏 App 作者及网站"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_enterprise
|
||||||
|
msgid "Uncheck to hide the Enterprise tag"
|
||||||
|
msgstr "不选中则会隐藏升级到企业版标签"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_share
|
||||||
|
msgid "Uncheck to hide the Odoo Share Dashboard"
|
||||||
|
msgstr "不选中则会隐藏odoo分享"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_poweredby
|
||||||
|
msgid "Uncheck to hide the Powered by text"
|
||||||
|
msgstr "不勾选则不显示Odoo Powered by"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__module_odoo_referral
|
||||||
|
msgid "Uncheck to remove the Odoo Referral"
|
||||||
|
msgstr "不勾选则不显示Odoo推荐"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_module_view_kanban
|
||||||
|
msgid "Uninstall"
|
||||||
|
msgstr "卸载"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.actions.server,name:app_odoo_customize.action_server_module_multi_uninstall
|
||||||
|
msgid "Uninstall Modules"
|
||||||
|
msgstr "卸载应用"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_base_module_update
|
||||||
|
msgid "Update Module"
|
||||||
|
msgstr "更新模块"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.app_module_view_kanban
|
||||||
|
msgid "Upgrade"
|
||||||
|
msgstr "升级"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__module_app_chatgpt
|
||||||
|
msgid "Use Ai to boost you business."
|
||||||
|
msgstr "使用 Ai 促进您的业务发展。"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "Use Ai to make odoo more powerful."
|
||||||
|
msgstr "使用 Ai 使odoo更强大好用。"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "User Menu"
|
||||||
|
msgstr "用户菜单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid "User Menu Content"
|
||||||
|
msgstr "菜单项具体操作设置"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_odoo_customize.view_app_theme_config_settings
|
||||||
|
msgid ""
|
||||||
|
"Visit our website for more apps and Support.\n"
|
||||||
|
"\t\t\t\t\t\t\t\t\t\t\t\t\thttps://www.odooai.cn"
|
||||||
|
msgstr "请访问我们的网站获取更多支持. http://www.odooai.cn"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model,name:app_odoo_customize.model_web_environment_ribbon_backend
|
||||||
|
msgid "Web Environment Ribbon Backend"
|
||||||
|
msgstr "全站测试标签"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_account
|
||||||
|
msgid "When enable,User can login to your website"
|
||||||
|
msgstr "启用后,会显示登录到你的网站"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_lang
|
||||||
|
msgid "When enable,User can quick switch language in user menu"
|
||||||
|
msgstr "启用后,会显示快速语言切换菜单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_documentation_dev
|
||||||
|
msgid "When enable,User can visit development documentation"
|
||||||
|
msgstr "启用后,会显示开发手册链接"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_documentation
|
||||||
|
msgid "When enable,User can visit user manual"
|
||||||
|
msgstr "启用后,会显示用户手册链接"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_support
|
||||||
|
msgid "When enable,User can vist your support site"
|
||||||
|
msgstr "启用后,会显示在线支持链接"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.model.fields,help:app_odoo_customize.field_res_config_settings__app_show_debug
|
||||||
|
msgid "When enable,everyone login can see the debug menu"
|
||||||
|
msgstr "启用后,会显示快速调试菜单"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#: model:ir.ui.menu,name:app_odoo_customize.menu_app_group
|
||||||
|
msgid "odooAi"
|
||||||
|
msgstr "odooAi"
|
||||||
|
|
||||||
|
#. module: app_odoo_customize
|
||||||
|
#. odoo-javascript
|
||||||
|
#: code:addons/app_odoo_customize/static/src/xml/res_config_edition.xml:0
|
||||||
|
#, python-format
|
||||||
|
msgid "odooai.cn"
|
||||||
|
msgstr "odooai.cn"
|
||||||
11
app_odoo_customize/models/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import res_config_settings
|
||||||
|
from . import base_language_install
|
||||||
|
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
|
||||||
8
app_odoo_customize/models/base_language_install.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
|
||||||
|
class BaseLanguageInstall(models.TransientModel):
|
||||||
|
_inherit = "base.language.install"
|
||||||
38
app_odoo_customize/models/ir_http.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
|
||||||
|
from odoo import models
|
||||||
|
from odoo.http import request
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class IrHttp(models.AbstractModel):
|
||||||
|
_inherit = 'ir.http'
|
||||||
|
|
||||||
|
def session_info(self):
|
||||||
|
result = super(IrHttp, self).session_info()
|
||||||
|
config_parameter = request.env['ir.config_parameter'].sudo()
|
||||||
|
result['app_system_name'] = config_parameter.get_param('app_system_name', 'odooAi')
|
||||||
|
result['app_documentation_url'] = config_parameter.get_param('app_documentation_url')
|
||||||
|
result['app_documentation_dev_url'] = config_parameter.get_param('app_documentation_dev_url')
|
||||||
|
result['app_support_url'] = config_parameter.get_param('app_support_url')
|
||||||
|
result['app_account_title'] = config_parameter.get_param('app_account_title')
|
||||||
|
result['app_account_url'] = config_parameter.get_param('app_account_url')
|
||||||
|
result['app_show_lang'] = config_parameter.get_param('app_show_lang')
|
||||||
|
result['app_show_debug'] = config_parameter.get_param('app_show_debug')
|
||||||
|
result['app_show_documentation'] = config_parameter.get_param('app_show_documentation')
|
||||||
|
result['app_show_documentation_dev'] = config_parameter.get_param('app_show_documentation_dev')
|
||||||
|
result['app_show_support'] = config_parameter.get_param('app_show_support')
|
||||||
|
result['app_show_account'] = config_parameter.get_param('app_show_account')
|
||||||
|
result['app_show_poweredby'] = config_parameter.get_param('app_show_poweredby')
|
||||||
|
# 增加多语言
|
||||||
|
result['app_lang_list'] = self.env['res.lang'].search_read([], ['id', 'code', 'name'])
|
||||||
|
result['is_erp_manager'] = self.env.user.has_group('base.group_erp_manager')
|
||||||
|
# 增加 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
|
||||||
41
app_odoo_customize/models/ir_module_addons_path.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import random
|
||||||
|
from odoo import api, fields, models, modules, tools, _
|
||||||
|
|
||||||
|
|
||||||
|
class IrModuleAddonsPath(models.Model):
|
||||||
|
_name = "ir.module.addons.path"
|
||||||
|
_description = 'Module Addons Path'
|
||||||
|
|
||||||
|
def _default_bg_color(self):
|
||||||
|
colors = ['#F06050', '#F4A45F', '#F7CD2E', '#6CC1ED', '#EB7E7F', '#5CC482',
|
||||||
|
'#2c8297', '#D8485E', '#9365B8', '#804967', '#475576', ]
|
||||||
|
res = '#FFFFFF'
|
||||||
|
try:
|
||||||
|
res = random.choice(colors)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return res
|
||||||
|
|
||||||
|
name = fields.Char(string='Short Name')
|
||||||
|
path = fields.Char(string='Path')
|
||||||
|
path_temp = fields.Char(string='Path Temp')
|
||||||
|
color = fields.Char(default=_default_bg_color)
|
||||||
|
module_ids = fields.One2many('ir.module.module', 'addons_path_id')
|
||||||
|
module_count = fields.Integer(compute='_compute_module_count')
|
||||||
|
|
||||||
|
def _compute_module_count(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.module_count = len(rec.module_ids)
|
||||||
|
|
||||||
|
def open_apps_view(self):
|
||||||
|
self.ensure_one()
|
||||||
|
|
||||||
|
return {'type': 'ir.actions.act_window',
|
||||||
|
'name': 'Apps',
|
||||||
|
'view_mode': 'kanban,tree,form',
|
||||||
|
'res_model': 'ir.module.module',
|
||||||
|
'context': {},
|
||||||
|
'domain': [('addons_path_id', '=', self.id)],
|
||||||
|
}
|
||||||
83
app_odoo_customize/models/ir_module_module.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, fields, models, modules, tools, _
|
||||||
|
|
||||||
|
import operator
|
||||||
|
|
||||||
|
|
||||||
|
class IrModule(models.Model):
|
||||||
|
_inherit = 'ir.module.module'
|
||||||
|
|
||||||
|
# attention: Incorrect field names !!
|
||||||
|
# installed_version refers the latest version (the one on disk)
|
||||||
|
# latest_version refers the installed version (the one in database)
|
||||||
|
# published_version refers the version available on the repository
|
||||||
|
# installed_version = fields.Char('Latest Version', compute='_get_latest_version')
|
||||||
|
# latest_version = fields.Char('Installed Version', readonly=True)
|
||||||
|
|
||||||
|
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
|
||||||
|
including the deletion of all database structures created by the module:
|
||||||
|
tables, columns, constraints, etc.
|
||||||
|
"""
|
||||||
|
modules = self.browse(self.env.context.get('active_ids'))
|
||||||
|
[module.button_immediate_uninstall() for module in modules if module not in ['base', 'web']]
|
||||||
|
|
||||||
|
# 更新翻译,当前语言
|
||||||
|
def module_multi_refresh_po(self):
|
||||||
|
lang = self.env.user.lang
|
||||||
|
modules = self.filtered(lambda r: r.state == 'installed')
|
||||||
|
# 先清理, odoo原生经常清理不干净
|
||||||
|
# odoo 16中,不再使用 ir.translation,直接使用json字段
|
||||||
|
# for rec in modules:
|
||||||
|
# translate = self.env['ir.translation'].search([
|
||||||
|
# ('lang', '=', lang),
|
||||||
|
# ('module', '=', rec.name)
|
||||||
|
# ])
|
||||||
|
# translate.sudo().unlink()
|
||||||
|
# 再重载
|
||||||
|
modules._update_translations(filter_lang=lang, overwrite=True)
|
||||||
|
# odoo 16翻译模式改变,仍需更新模块
|
||||||
|
return {
|
||||||
|
'type': 'ir.actions.client',
|
||||||
|
'tag': 'display_notification',
|
||||||
|
'target': 'new',
|
||||||
|
'params': {
|
||||||
|
'message': _("The languages that you selected have been successfully update.\
|
||||||
|
You still need to Upgrade the apps to make it worked."),
|
||||||
|
'type': 'success',
|
||||||
|
'sticky': False,
|
||||||
|
'next': {'type': 'ir.actions.act_window_close'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def button_get_po(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env.ref('app_odoo_customize.action_server_module_multi_get_po').read()[0]
|
||||||
|
action['context'].update({
|
||||||
|
'default_lang': self.env.user.lang,
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
|
||||||
|
def update_list(self):
|
||||||
|
res = super(IrModule, self).update_list()
|
||||||
|
default_version = modules.adapt_version('1.0')
|
||||||
|
known_mods = self.with_context(lang=None).search([])
|
||||||
|
known_mods_names = {mod.name: mod for mod in known_mods}
|
||||||
|
# 处理可更新字段, 不要compute,会出错
|
||||||
|
for mod_name in modules.get_modules():
|
||||||
|
mod = known_mods_names.get(mod_name)
|
||||||
|
installed_version = self.get_module_info(mod.name).get('version', default_version)
|
||||||
|
if installed_version and mod.latest_version and operator.gt(installed_version, mod.latest_version):
|
||||||
|
local_updatable = True
|
||||||
|
else:
|
||||||
|
local_updatable = False
|
||||||
|
if mod.local_updatable != local_updatable:
|
||||||
|
mod.write({'local_updatable': local_updatable})
|
||||||
|
|
||||||
|
return res
|
||||||
26
app_odoo_customize/models/ir_ui_menu.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from odoo import api, fields, models, tools, _
|
||||||
|
|
||||||
|
MENU_ITEM_SEPARATOR = "/"
|
||||||
|
NUMBER_PARENS = re.compile(r"\(([0-9]+)\)")
|
||||||
|
|
||||||
|
|
||||||
|
class IrUiMenu(models.Model):
|
||||||
|
_inherit = 'ir.ui.menu'
|
||||||
|
|
||||||
|
def _get_full_name(self, level=6):
|
||||||
|
""" Return the full name of ``self`` (up to a certain level). """
|
||||||
|
if level <= 0:
|
||||||
|
return '...'
|
||||||
|
if self.parent_id:
|
||||||
|
try:
|
||||||
|
name = self.parent_id._get_full_name(level - 1) + MENU_ITEM_SEPARATOR + (self.name or "")
|
||||||
|
except Exception:
|
||||||
|
name = self.name or "..."
|
||||||
|
else:
|
||||||
|
name = self.name
|
||||||
|
return name
|
||||||
|
|
||||||
17
app_odoo_customize/models/ir_ui_view.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class View(models.Model):
|
||||||
|
_inherit = 'ir.ui.view'
|
||||||
|
|
||||||
|
def _render_template(self, template, values=None, engine='ir.qweb'):
|
||||||
|
# if template in ['web.login', 'web.webclient_bootstrap']:
|
||||||
|
if not values:
|
||||||
|
values = {}
|
||||||
|
values["title"] = values["app_title"] = self.env['ir.config_parameter'].sudo().get_param("app_system_name", "odooAi")
|
||||||
|
return super(View, self)._render_template(template, values=values, engine=engine)
|
||||||
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)
|
||||||
512
app_odoo_customize/models/res_config_settings.py
Normal file
@@ -0,0 +1,512 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
from odoo.exceptions import UserError, ValidationError
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ResConfigSettings(models.TransientModel):
|
||||||
|
_inherit = 'res.config.settings'
|
||||||
|
|
||||||
|
app_system_name = fields.Char('System Name', help="Setup System Name,which replace Odoo",
|
||||||
|
default='odooAi', config_parameter='app_system_name')
|
||||||
|
app_show_lang = fields.Boolean('Show Quick Language Switcher',
|
||||||
|
help="When enable,User can quick switch language in user menu",
|
||||||
|
config_parameter='app_show_lang')
|
||||||
|
app_show_debug = fields.Boolean('Show Quick Debug', help="When enable,everyone login can see the debug menu",
|
||||||
|
config_parameter='app_show_debug')
|
||||||
|
app_show_documentation = fields.Boolean('Show Documentation', help="When enable,User can visit user manual",
|
||||||
|
config_parameter='app_show_documentation')
|
||||||
|
# 停用
|
||||||
|
app_show_documentation_dev = fields.Boolean('Show Developer Documentation',
|
||||||
|
help="When enable,User can visit development documentation")
|
||||||
|
app_show_support = fields.Boolean('Show Support', help="When enable,User can vist your support site",
|
||||||
|
config_parameter='app_show_support')
|
||||||
|
app_show_account = fields.Boolean('Show My Account', help="When enable,User can login to your website",
|
||||||
|
config_parameter='app_show_account')
|
||||||
|
app_show_enterprise = fields.Boolean('Show Enterprise Tag', help="Uncheck to hide the Enterprise tag",
|
||||||
|
config_parameter='app_show_enterprise')
|
||||||
|
app_show_share = fields.Boolean('Show Share Dashboard', help="Uncheck to hide the Odoo Share Dashboard",
|
||||||
|
config_parameter='app_show_share')
|
||||||
|
app_show_poweredby = fields.Boolean('Show Powered by Odoo', help="Uncheck to hide the Powered by text",
|
||||||
|
config_parameter='app_show_poweredby')
|
||||||
|
group_show_author_in_apps = fields.Boolean(string="Show Author in Apps Dashboard", implied_group='app_odoo_customize.group_show_author_in_apps',
|
||||||
|
help="Uncheck to Hide Author and Website in Apps Dashboard")
|
||||||
|
module_odoo_referral = fields.Boolean('Show Odoo Referral', help="Uncheck to remove the Odoo Referral")
|
||||||
|
|
||||||
|
app_documentation_url = fields.Char('Documentation Url', config_parameter='app_documentation_url')
|
||||||
|
app_documentation_dev_url = fields.Char('Developer Documentation Url', config_parameter='app_documentation_dev_url')
|
||||||
|
app_support_url = fields.Char('Support Url', config_parameter='app_support_url')
|
||||||
|
app_account_title = fields.Char('My Odoo.com Account Title', config_parameter='app_account_title')
|
||||||
|
app_account_url = fields.Char('My Odoo.com Account Url', config_parameter='app_account_url')
|
||||||
|
app_enterprise_url = fields.Char('Customize Module Url(eg. Enterprise)', config_parameter='app_enterprise_url')
|
||||||
|
app_ribbon_name = fields.Char('Show Demo Ribbon', config_parameter='app_ribbon_name')
|
||||||
|
app_navbar_pos_pc = fields.Selection(string="Navbar PC", selection=[
|
||||||
|
('top', 'Top(Default)'),
|
||||||
|
('bottom', 'Bottom'),
|
||||||
|
# ('left', 'Left'),
|
||||||
|
], config_parameter='app_navbar_pos_pc')
|
||||||
|
app_navbar_pos_mobile = fields.Selection(string="Navbar Mobile", selection=[
|
||||||
|
('top', 'Top(Default)'),
|
||||||
|
('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')
|
||||||
|
# 处理额外模块
|
||||||
|
module_app_odoo_doc = fields.Boolean("Help Document Anywhere", help='Get Help Documentation on current odoo operation or topic.')
|
||||||
|
module_app_chatgpt = fields.Boolean("Ai Center", help='Use Ai to boost you business.')
|
||||||
|
|
||||||
|
# 应用帮助文档
|
||||||
|
app_doc_root_url = fields.Char('Help of topic domain', config_parameter='app_doc_root_url', default='https://odooai.cn')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def set_module_url(self, rec=None):
|
||||||
|
config_parameter = self.env['ir.config_parameter'].sudo()
|
||||||
|
app_enterprise_url = config_parameter.get_param('app_enterprise_url', 'https://www.odooai.cn')
|
||||||
|
sql = "UPDATE ir_module_module SET website = '%s' WHERE license like '%s' and website <> ''" % (app_enterprise_url, 'OEEL%')
|
||||||
|
try:
|
||||||
|
self._cr.execute(sql)
|
||||||
|
self._cr.commit()
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 清数据,o=对象, s=序列
|
||||||
|
def remove_app_data(self, o, s=[]):
|
||||||
|
for line in o:
|
||||||
|
# 检查是否存在
|
||||||
|
try:
|
||||||
|
if not self.env['ir.model']._get(line):
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
_logger.warning('remove data error get ir.model: %s,%s', line, e)
|
||||||
|
continue
|
||||||
|
obj_name = line
|
||||||
|
obj = self.pool.get(obj_name)
|
||||||
|
if not obj:
|
||||||
|
# 有时安装出错数据乱,没有 obj 但有 table
|
||||||
|
t_name = obj_name.replace('.', '_')
|
||||||
|
else:
|
||||||
|
t_name = obj._table
|
||||||
|
|
||||||
|
sql = "delete from %s" % t_name
|
||||||
|
# 增加多公司处理
|
||||||
|
try:
|
||||||
|
self._cr.execute(sql)
|
||||||
|
self._cr.commit()
|
||||||
|
except Exception as e:
|
||||||
|
_logger.warning('remove data error: %s,%s', line, e)
|
||||||
|
# 更新序号
|
||||||
|
for line in s:
|
||||||
|
domain = ['|', ('code', '=ilike', line + '%'), ('prefix', '=ilike', line + '%')]
|
||||||
|
try:
|
||||||
|
seqs = self.env['ir.sequence'].sudo().search(domain)
|
||||||
|
if seqs.exists():
|
||||||
|
seqs.write({
|
||||||
|
'number_next': 1,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
_logger.warning('reset sequence data error: %s,%s', line, e)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def remove_sales(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除销售单据
|
||||||
|
'sale.order.line',
|
||||||
|
'sale.order',
|
||||||
|
# 销售提成,自用
|
||||||
|
# 'sale.commission.line',
|
||||||
|
# 不能删除报价单模板
|
||||||
|
'sale.order.template.option',
|
||||||
|
'sale.order.template.line',
|
||||||
|
'sale.order.template',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'sale',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_product(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除产品数据
|
||||||
|
'product.product',
|
||||||
|
'product.template',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'product.product',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_product_attribute(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除产品属性
|
||||||
|
'product.attribute.value',
|
||||||
|
'product.attribute',
|
||||||
|
]
|
||||||
|
seqs = []
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_pos(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除POS单据
|
||||||
|
'pos.payment',
|
||||||
|
'pos.order.line',
|
||||||
|
'pos.order',
|
||||||
|
'pos.session',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'pos.',
|
||||||
|
]
|
||||||
|
res = self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
# 更新要关帐的值,因为 store=true 的计算字段要重置
|
||||||
|
|
||||||
|
try:
|
||||||
|
statement = self.env['account.bank.statement'].sudo().search([])
|
||||||
|
for s in statement:
|
||||||
|
s._end_balance()
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error('reset sequence data error: %s', e)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def remove_purchase(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除采购单据
|
||||||
|
'purchase.order.line',
|
||||||
|
'purchase.order',
|
||||||
|
'purchase.requisition.line',
|
||||||
|
'purchase.requisition',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'purchase.',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_expense(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除
|
||||||
|
'hr.expense.sheet',
|
||||||
|
'hr.expense',
|
||||||
|
'hr.payslip',
|
||||||
|
'hr.payslip.run',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'hr.expense.',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_mrp(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除生产单据
|
||||||
|
'mrp.workcenter.productivity',
|
||||||
|
'mrp.workorder',
|
||||||
|
# 'mrp.production.workcenter.line',
|
||||||
|
'change.production.qty',
|
||||||
|
'mrp.production',
|
||||||
|
# 'mrp.production.product.line',
|
||||||
|
'mrp.unbuild',
|
||||||
|
'change.production.qty',
|
||||||
|
# 'sale.forecast.indirect',
|
||||||
|
# 'sale.forecast',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'mrp.',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_mrp_bom(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除生产BOM
|
||||||
|
'mrp.bom.line',
|
||||||
|
'mrp.bom',
|
||||||
|
]
|
||||||
|
seqs = []
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_inventory(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除库存单据
|
||||||
|
'stock.quant',
|
||||||
|
'stock.move.line',
|
||||||
|
'stock.package_level',
|
||||||
|
'stock.quantity.history',
|
||||||
|
'stock.quant.package',
|
||||||
|
'stock.move',
|
||||||
|
# 'stock.pack.operation',
|
||||||
|
'stock.picking',
|
||||||
|
'stock.scrap',
|
||||||
|
'stock.picking.batch',
|
||||||
|
'stock.inventory.adjustment.name',
|
||||||
|
'stock.valuation.layer',
|
||||||
|
'stock.lot',
|
||||||
|
# 'stock.fixed.putaway.strat',
|
||||||
|
'procurement.group',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'stock.',
|
||||||
|
'picking.',
|
||||||
|
'procurement.group',
|
||||||
|
'product.tracking.default',
|
||||||
|
'WH/',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_account(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除财务会计单据
|
||||||
|
'payment.transaction',
|
||||||
|
# 'account.voucher.line',
|
||||||
|
# 'account.voucher',
|
||||||
|
# 'account.invoice.line',
|
||||||
|
# 'account.invoice.refund',
|
||||||
|
# 'account.invoice',
|
||||||
|
'account.bank.statement.line',
|
||||||
|
'account.payment',
|
||||||
|
'account.batch.payment',
|
||||||
|
'account.analytic.line',
|
||||||
|
'account.analytic.account',
|
||||||
|
'account.partial.reconcile',
|
||||||
|
'account.move.line',
|
||||||
|
'hr.expense.sheet',
|
||||||
|
'account.move',
|
||||||
|
]
|
||||||
|
res = self.remove_app_data(to_removes, [])
|
||||||
|
|
||||||
|
# extra 更新序号
|
||||||
|
domain = [
|
||||||
|
('company_id', '=', self.env.company.id),
|
||||||
|
'|', ('code', '=ilike', 'account.%'),
|
||||||
|
'|', ('prefix', '=ilike', 'BNK1/%'),
|
||||||
|
'|', ('prefix', '=ilike', 'CSH1/%'),
|
||||||
|
'|', ('prefix', '=ilike', 'INV/%'),
|
||||||
|
'|', ('prefix', '=ilike', 'EXCH/%'),
|
||||||
|
'|', ('prefix', '=ilike', 'MISC/%'),
|
||||||
|
'|', ('prefix', '=ilike', '账单/%'),
|
||||||
|
('prefix', '=ilike', '杂项/%')
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
seqs = self.env['ir.sequence'].search(domain)
|
||||||
|
if seqs.exists():
|
||||||
|
seqs.write({
|
||||||
|
'number_next': 1,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error('reset sequence data error: %s,%s', domain, e)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def remove_account_chart(self):
|
||||||
|
company_id = self.env.company.id
|
||||||
|
self = self.with_company(self.env.company)
|
||||||
|
to_removes = [
|
||||||
|
# 清除财务科目,用于重设
|
||||||
|
'res.partner.bank',
|
||||||
|
# 'account.invoice',
|
||||||
|
'account.payment',
|
||||||
|
'account.bank.statement',
|
||||||
|
# 'account.tax.account.tag',
|
||||||
|
'account.tax',
|
||||||
|
# 'wizard_multi_charts_accounts',
|
||||||
|
'account.account',
|
||||||
|
]
|
||||||
|
# todo: 要做 remove_hr,因为工资表会用到 account
|
||||||
|
# 更新account关联,很多是多公司字段,故只存在 ir_property,故在原模型,只能用update
|
||||||
|
try:
|
||||||
|
field1 = self.env['ir.model.fields']._get('product.template', "taxes_id").id
|
||||||
|
field2 = self.env['ir.model.fields']._get('product.template', "supplier_taxes_id").id
|
||||||
|
|
||||||
|
sql = "delete from ir_default where (field_id = %s or field_id = %s) and company_id=%d" \
|
||||||
|
% (field1, field2, company_id)
|
||||||
|
sql2 = "update account_journal set bank_account_id=NULL where company_id=%d;" % company_id
|
||||||
|
self._cr.execute(sql)
|
||||||
|
self._cr.execute(sql2)
|
||||||
|
|
||||||
|
self._cr.commit()
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error('remove data error: %s,%s', 'account_chart: set tax and account_journal', e)
|
||||||
|
|
||||||
|
# 增加对 pos的处理
|
||||||
|
if self.env['ir.model']._get('pos.config'):
|
||||||
|
self.env['pos.config'].write({
|
||||||
|
'journal_id': False,
|
||||||
|
})
|
||||||
|
# todo: 以下处理参考 res.partner的合并,将所有m2o的都一次处理,不需要次次找模型
|
||||||
|
# partner 处理
|
||||||
|
try:
|
||||||
|
rec = self.env['res.partner'].search([])
|
||||||
|
for r in rec:
|
||||||
|
r.write({
|
||||||
|
'property_account_receivable_id': None,
|
||||||
|
'property_account_payable_id': None,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error('remove data error: %s,%s', 'account_chart', e)
|
||||||
|
# 品类处理
|
||||||
|
try:
|
||||||
|
rec = self.env['product.category'].search([])
|
||||||
|
for r in rec:
|
||||||
|
r.write({
|
||||||
|
'property_account_income_categ_id': None,
|
||||||
|
'property_account_expense_categ_id': None,
|
||||||
|
'property_account_creditor_price_difference_categ': None,
|
||||||
|
'property_stock_account_input_categ_id': None,
|
||||||
|
'property_stock_account_output_categ_id': None,
|
||||||
|
'property_stock_valuation_account_id': None,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
# 产品处理
|
||||||
|
try:
|
||||||
|
rec = self.env['product.template'].search([])
|
||||||
|
for r in rec:
|
||||||
|
r.write({
|
||||||
|
'property_account_income_id': None,
|
||||||
|
'property_account_expense_id': None,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
# 库存计价处理
|
||||||
|
try:
|
||||||
|
rec = self.env['stock.location'].search([])
|
||||||
|
for r in rec:
|
||||||
|
r.write({
|
||||||
|
'valuation_in_account_id': None,
|
||||||
|
'valuation_out_account_id': None,
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
pass # raise Warning(e)
|
||||||
|
|
||||||
|
seqs = []
|
||||||
|
self.env.company.write({
|
||||||
|
'chart_template_id': False,
|
||||||
|
})
|
||||||
|
res = self.remove_app_data(to_removes, seqs)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def remove_project(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除项目
|
||||||
|
'account.analytic.line',
|
||||||
|
'project.task',
|
||||||
|
# 'project.forecast',
|
||||||
|
'project.project',
|
||||||
|
]
|
||||||
|
seqs = []
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_quality(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除质检数据
|
||||||
|
'quality.check',
|
||||||
|
'quality.alert',
|
||||||
|
# 'quality.point',
|
||||||
|
# 'quality.alert.stage',
|
||||||
|
# 'quality.alert.team',
|
||||||
|
# 'quality.point.test_type',
|
||||||
|
# 'quality.reason',
|
||||||
|
# 'quality.tag',
|
||||||
|
]
|
||||||
|
seqs = [
|
||||||
|
'quality.check',
|
||||||
|
'quality.alert',
|
||||||
|
# 'quality.point',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_quality_setting(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除质检设置
|
||||||
|
'quality.point',
|
||||||
|
'quality.alert.stage',
|
||||||
|
'quality.alert.team',
|
||||||
|
'quality.point.test_type',
|
||||||
|
'quality.reason',
|
||||||
|
'quality.tag',
|
||||||
|
]
|
||||||
|
return self.remove_app_data(to_removes)
|
||||||
|
|
||||||
|
def remove_website(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除网站数据,w, w_blog
|
||||||
|
'blog.tag.category',
|
||||||
|
'blog.tag',
|
||||||
|
'blog.post',
|
||||||
|
'blog.blog',
|
||||||
|
'product.wishlist',
|
||||||
|
'website.published.multi.mixin',
|
||||||
|
'website.published.mixin',
|
||||||
|
'website.multi.mixin',
|
||||||
|
'website.visitor',
|
||||||
|
'website.rewrite',
|
||||||
|
'website.seo.metadata',
|
||||||
|
# 'website.page',
|
||||||
|
# 'website.menu',
|
||||||
|
# 'website',
|
||||||
|
]
|
||||||
|
seqs = []
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_message(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除消息数据
|
||||||
|
'mail.message',
|
||||||
|
'mail.followers',
|
||||||
|
'mail.activity',
|
||||||
|
]
|
||||||
|
seqs = []
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_workflow(self):
|
||||||
|
to_removes = [
|
||||||
|
# 清除工作流
|
||||||
|
# 'wkf.workitem',
|
||||||
|
# 'wkf.instance',
|
||||||
|
]
|
||||||
|
seqs = []
|
||||||
|
return self.remove_app_data(to_removes, seqs)
|
||||||
|
|
||||||
|
def remove_all_biz(self):
|
||||||
|
self.remove_account()
|
||||||
|
self.remove_quality()
|
||||||
|
self.remove_inventory()
|
||||||
|
self.remove_purchase()
|
||||||
|
self.remove_mrp()
|
||||||
|
self.remove_sales()
|
||||||
|
self.remove_project()
|
||||||
|
self.remove_pos()
|
||||||
|
self.remove_expense()
|
||||||
|
self.remove_message()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def reset_cat_loc_name(self):
|
||||||
|
ids = self.env['product.category'].search([
|
||||||
|
('parent_id', '!=', False)
|
||||||
|
], order='complete_name')
|
||||||
|
for rec in ids:
|
||||||
|
try:
|
||||||
|
rec._compute_complete_name()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
ids = self.env['stock.location'].search([
|
||||||
|
('location_id', '!=', False),
|
||||||
|
('usage', '!=', 'views'),
|
||||||
|
], order='complete_name')
|
||||||
|
for rec in ids:
|
||||||
|
try:
|
||||||
|
rec._compute_complete_name()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action_set_app_doc_root_to_my(self):
|
||||||
|
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
||||||
|
self.app_doc_root_url = base_url
|
||||||
|
|
||||||
|
# def action_set_all_to_app_doc_root_url(self):
|
||||||
|
# if self.app_doc_root_url:
|
||||||
36
app_odoo_customize/models/web_environment_ribbon_backend.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Copyright 2017 ACSONE SA/NV
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, models
|
||||||
|
|
||||||
|
|
||||||
|
class WebEnvironmentRibbonBackend(models.AbstractModel):
|
||||||
|
|
||||||
|
_name = "web.environment.ribbon.backend"
|
||||||
|
_description = "Web Environment Ribbon Backend"
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _prepare_ribbon_format_vals(self):
|
||||||
|
return {"db_name": self.env.cr.dbname}
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _prepare_ribbon_name(self):
|
||||||
|
name_tmpl = self.env["ir.config_parameter"].sudo().get_param("app_ribbon_name") or False
|
||||||
|
vals = self._prepare_ribbon_format_vals()
|
||||||
|
return name_tmpl and name_tmpl.format(**vals) or name_tmpl
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def get_environment_ribbon(self):
|
||||||
|
"""
|
||||||
|
This method returns the ribbon data from ir config parameters
|
||||||
|
:return: dictionary
|
||||||
|
"""
|
||||||
|
ir_config_model = self.env["ir.config_parameter"]
|
||||||
|
name = self._prepare_ribbon_name()
|
||||||
|
return {
|
||||||
|
"name": name,
|
||||||
|
"color": ir_config_model.sudo().get_param("app_ribbon_color"),
|
||||||
|
"background_color": ir_config_model.sudo().get_param(
|
||||||
|
"app_ribbon_background_color"
|
||||||
|
),
|
||||||
|
}
|
||||||
92
app_odoo_customize/readme.md
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
##App Odoo Customize(Debranding Title,Language,Documentation,Quick Debug,Quick Data Clear)
|
||||||
|
============
|
||||||
|
White label odoo.
|
||||||
|
Support Odoo 13,12, 11, 10, 9. Including communicate and enterprise version.
|
||||||
|
You can config odoo, make it look like your own platform.
|
||||||
|
1. Deletes Odoo label in footer
|
||||||
|
2. Replaces "Odoo" in Windows title
|
||||||
|
3. Customize Documentation, Support, About links and title in usermenu
|
||||||
|
4. Adds "Developer mode" link to the top right-hand User Menu.
|
||||||
|
5. Adds Quick Language Switcher to the top right-hand User Menu.
|
||||||
|
6. Adds Country flags to the top right-hand User Menu.
|
||||||
|
7. Adds English and Chinese user documentation access to the top right-hand User Menu.
|
||||||
|
8. Adds developer documentation access to the top right-hand User Menu.
|
||||||
|
9. Customize "My odoo.com account" button
|
||||||
|
10. Standalone setting panel, easy to setup.
|
||||||
|
11. Provide 236 country flags.
|
||||||
|
12. Multi-language Support.
|
||||||
|
13. Change Powered by Odoo in login screen.(Please change '../views/app_odoo_customize_view.xml' #15)
|
||||||
|
14. Quick delete test data in Apps: Sales/POS/Purchase/MRP/Inventory/Accounting/Project/Message/Workflow etc.
|
||||||
|
15. Reset All the Sequence to beginning of 1: SO/PO/MO/Invoice...
|
||||||
|
16. Fix odoo reload module translation bug while enable english language
|
||||||
|
17. Stop Odoo Auto Subscribe(Performance Improve)
|
||||||
|
18. Show/Hide Author and Website in Apps Dashboard
|
||||||
|
19. One Click to clear all data (Sometime pls click twice)
|
||||||
|
20. Show quick upgrade in app dashboard, click to show module info not go to odoo.com
|
||||||
|
21. Can clear and reset account chart. Be cautious
|
||||||
|
22. Update online manual and developer document to odoo12
|
||||||
|
23. Add reset or clear website blog data
|
||||||
|
24. Customize Odoo Native Module(eg. Enterprise) Url
|
||||||
|
25. Add remove expense data
|
||||||
|
26. Add multi uninstall modules
|
||||||
|
27. Add odoo boost modules link.
|
||||||
|
|
||||||
|
This module can help to white label the Odoo.
|
||||||
|
Also helpful for training and support for your odoo end-user.
|
||||||
|
The user can get the help document just by one click.
|
||||||
|
|
||||||
|
For more support
|
||||||
|
https://www.odooai.cn
|
||||||
|
|
||||||
|
## 在符合odoo开源协议的前提下,去除odoo版权信息,自定义你的odoo
|
||||||
|
可完全自行设置下列 odoo 选项,让 odoo 看上去像是你的软件产品
|
||||||
|
支持Odoo 13,12, 11, 10, 9 版本,社区版企业版通用
|
||||||
|
|
||||||
|
1. 删除菜单导航页脚的 Odoo 标签
|
||||||
|
2. 将弹出窗口中 "Odoo" 设置为自定义名称
|
||||||
|
3. 自定义用户菜单中的 Documentation, Support, About 的链接
|
||||||
|
4. 在用户菜单中增加快速切换开发模式
|
||||||
|
5. 在用户菜单中增加快速切换多国语言
|
||||||
|
6. 对语言菜单进行美化,设置国旗图标
|
||||||
|
7. 在用户菜单中增加中/英文用户手册,可以不用翻墙加速了
|
||||||
|
8. 在用户菜单中增加开发者手册,含python教程,jquery参考,Jinja2模板,PostgresSQL参考
|
||||||
|
9. 在用户菜单中自定义"My odoo.com account"
|
||||||
|
10. 单独设置面板,每个选项都可以自定义
|
||||||
|
11. 提供236个国家的国旗文件(部份需要自行设置文件名)
|
||||||
|
12. 多语言版本
|
||||||
|
13. 自定义登陆界面中的 Powered by Odoo
|
||||||
|
14. 快速删除测试数据,支持模块包括:销售/POS门店/采购/生产/库存/会计/项目/消息与工作流等.
|
||||||
|
15. 将各类单据的序号重置,从1开始,包括:SO/PO/MO/Invoice 等
|
||||||
|
16. 修复odoo启用英文后模块不显示中文的Bug
|
||||||
|
17. 可停用odoo自动订阅功能,避免“同样对象关注2次”bug,同时提升性能
|
||||||
|
18. 显示/隐藏应用的作者和网站-在应用安装面板中
|
||||||
|
19. 一键清除所有数据(视当前数据情况,有时需点击2次)
|
||||||
|
20. 在应用面板显示快速升级按键,点击时不会导航至 odoo.com
|
||||||
|
21. 清除并重置会计科目表
|
||||||
|
22. 全新升级将odoo12用户及开发手册导航至国内网站,或者自己定义的网站
|
||||||
|
23. 增加清除网站数据功能
|
||||||
|
24. 自定义 odoo 原生模块跳转的url(比如企业版模块)
|
||||||
|
25. 增加删除费用报销数据功能
|
||||||
|
26. 增加批量卸载模块功能
|
||||||
|
27. 增加odoo加速功能
|
||||||
|
|
||||||
|
使用方法:将解压后的 app_odoo_customize 放到 odoo的 addons目录下,激活开发者模式,应用-->更新应用列表,
|
||||||
|
找到 "App odoo Customize"模块,安装即可。
|
||||||
|
|
||||||
|
|
||||||
|
## 其它技术资源:
|
||||||
|
# Odoo12在线用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/12.0/en/index.html
|
||||||
|
|
||||||
|
# Odoo12在线开发者手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/12.0/index.html
|
||||||
|
|
||||||
|
# Odoo10在线中文用户手册(长期更新)
|
||||||
|
# https://www.odooai.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.odooai.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.odooai.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
3
app_odoo_customize/security/ir.model.access.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_ir_config_parameter_system,ir_config_parameter_system,base.model_ir_config_parameter,base.group_user,1,0,0,0
|
||||||
|
access_ir_module_addons_path_user,ir_module_addons_path_user,app_odoo_customize.model_ir_module_addons_path,base.group_system,1,1,1,1
|
||||||
|
19
app_odoo_customize/security/res_groups.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<openerp>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="group_show_author_in_apps" model="res.groups">
|
||||||
|
<field name="name">Show Author in Apps Dashboard</field>
|
||||||
|
<field name="category_id" ref="base.module_category_hidden"/>
|
||||||
|
</record>
|
||||||
|
<record id="group_show_quick_upgrade" model="res.groups">
|
||||||
|
<field name="name">Show Quick Upgrade in Apps Dashboard</field>
|
||||||
|
<field name="category_id" ref="base.module_category_hidden"/>
|
||||||
|
</record>
|
||||||
|
<!--系统设置默认值-->
|
||||||
|
<!--1 默认显示快速升级-->
|
||||||
|
<record id="base.group_user" model="res.groups">
|
||||||
|
<field name="implied_ids"
|
||||||
|
eval="[(4, ref('app_odoo_customize.group_show_quick_upgrade'))]"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</openerp>
|
||||||
BIN
app_odoo_customize/static/description/app_web_enterprise_03.jpg
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
app_odoo_customize/static/description/banner.gif
Normal file
|
After Width: | Height: | Size: 502 KiB |
BIN
app_odoo_customize/static/description/banner.png
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
app_odoo_customize/static/description/banner_ai.png
Normal file
|
After Width: | Height: | Size: 563 KiB |
BIN
app_odoo_customize/static/description/cnreadme.png
Normal file
|
After Width: | Height: | Size: 258 KiB |
BIN
app_odoo_customize/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
663
app_odoo_customize/static/description/index.html
Normal file
@@ -0,0 +1,663 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (C) 2009~2023 odooAi.cn
|
||||||
|
##############################################################################
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<!-- Modules Title and brief -->
|
||||||
|
<section class="container app">
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan">odoo Tweak,Ai Employee,Boost,Customize All in One. OEM,UI,Boost,Security,Data,Development Enhance</h2>
|
||||||
|
<h4 class="mt8">
|
||||||
|
You can follow this repo on github. To get the latest update of free odoo app.
|
||||||
|
<p>https://github.com/guohuadeng/app-odoo</p>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container app">
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan">This is a Long Term Support Apps.Update: v16.23.09.13</h2>
|
||||||
|
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item">1. Deletes Odoo label in footer</li>
|
||||||
|
<li class="list-group-item">2. Replaces "Odoo" in Windows title</li>
|
||||||
|
<li class="list-group-item">3. Customize Documentation, Support, About links and title in usermenu</li>
|
||||||
|
<li class="list-group-item">4. Adds "Developer mode" link to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">5. Adds Quick Language Switcher to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">6. Adds Country flags to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">7. Adds English and Chinese user documentation access to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">8. Adds developer documentation access to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">9. Customize "My odoo.com account" button</li>
|
||||||
|
<li class="list-group-item">10. Standalone setting panel, easy to setup.</li>
|
||||||
|
<li class="list-group-item">11. Provide 236 country flags.</li>
|
||||||
|
<li class="list-group-item">12. Multi-language Support.</li>
|
||||||
|
<li class="list-group-item">13. Change Powered by Odoo in login screen.(Please change '../views/app_odoo_customize_view.xml' #15)</li>
|
||||||
|
<li class="list-group-item">14. Quick delete test data in Apps: Sales/POS/Purchase/MRP/Inventory/Accounting/Project/Base Models.</li>
|
||||||
|
<li class="list-group-item">15. Reset All the Sequence to beginning of 1: SO/PO/MO/Invoice...</li>
|
||||||
|
<li class="list-group-item">16. Fix odoo reload module translation bug while enable english language</li>
|
||||||
|
<li class="list-group-item">17. Stop Odoo Auto Subscribe(Performance Improve)</li>
|
||||||
|
<li class="list-group-item">18. Show/Hide Author and Website in Apps Dashboard (odoo 11 only)</li>
|
||||||
|
<li class="list-group-item">19. One Click to clear all data (Sometime pls click twice)</li>
|
||||||
|
<li class="list-group-item">20. Show quick upgrade in app dashboard, click to show module info not go to odoo.com</li>
|
||||||
|
<li class="list-group-item">21. Can clear and reset account chart. Be cautious.</li>
|
||||||
|
<li class="list-group-item">22. Update online manual and developer document to odoo12.</li>
|
||||||
|
<li class="list-group-item">23. Add reset or clear website blog data</li>
|
||||||
|
<li class="list-group-item">24. Customize Odoo Native Module(eg. Enterprise) Url</li>
|
||||||
|
<li class="list-group-item">25. Add remove expense data</li>
|
||||||
|
<li class="list-group-item">26. Add multi uninstall modules</li>
|
||||||
|
<li class="list-group-item">27. Add odoo boost modules link.</li>
|
||||||
|
<li class="list-group-item">28. Easy Menu manager.</li>
|
||||||
|
<li class="list-group-item">29. Apps version compare. Add Install version in App list. Add Local updatable filter in app list.</li>
|
||||||
|
<li class="list-group-item">30. 1 key export app translate file like .po file.</li>
|
||||||
|
<li class="list-group-item">32. Fix odoo bug of complete name bug of product category and stock location..</li>
|
||||||
|
<li class="list-group-item">33. Add Demo Ribbon Setting.</li>
|
||||||
|
<li class="list-group-item">34. Add Remove all quality data.</li>
|
||||||
|
<li class="list-group-item">35. Fixed for odoo 14.</li>
|
||||||
|
<li class="list-group-item">36. Add refresh translate for multi module.</li>
|
||||||
|
<li class="list-group-item">37. Easy noupdate manage for External Identifiers(xml_id).</li>
|
||||||
|
<li class="list-group-item">38. Add Draggable Dialog enable.</li>
|
||||||
|
<li class="list-group-item">39. Only erp manager can see debug menu.</li>
|
||||||
|
<li class="list-group-item">40. Fix support for enterprise version.</li>
|
||||||
|
<li class="list-group-item">41. Fix odoo bug, when click Preferences menu not hide in mobile.</li>
|
||||||
|
<li class="list-group-item">42. Add menu navbar setup for top or bottom. navigator footer support.</li>
|
||||||
|
<li class="list-group-item">43. Check to only Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.</li>
|
||||||
|
<li class="list-group-item">44. Check to stop subscribe and follow. This to make odoo speed up.</li>
|
||||||
|
<li class="list-group-item">45. Add addons path info to module.</li>
|
||||||
|
<li class="list-group-item">46. Add Help documentation anywhere. easy get help for any odoo operation or action.</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||||
|
<p>If you want to change the login page. </p>
|
||||||
|
<p class="mb16">Please modify the file \views\app_odoo_customize_views.xml </p>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
This module can help to white label the Odoo.
|
||||||
|
Also helpful for training and support for your odoo end-user.<br/>
|
||||||
|
The user can get the help document just by one click.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Help Document Anywhere</h2>
|
||||||
|
<h4 class="oe_slogan">Get Help Documentation on current odoo operation or topic.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp1.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">You can use you company logo for the document with you help document</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp2.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">You can set extra help doc for any addons</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp3.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Must in development mode</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp4.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">New Ai Center Support</h2>
|
||||||
|
<h4 class="oe_slogan">You can install Ai service like chatgpt and google bard and azure openai.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setai1.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Odoo Customize(Debranding Title,Language,Documentation,Quick Debug)</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="banner.gif" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Multi-language support: Chinese ready</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setcn1.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
<img src="setcn2.png" style="border:1px solid black"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style=" argin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%; ">
|
||||||
|
<h2 class='oe_mt32'>How to use: Go to Settings -> odooAi -> Customize Boost Odoo</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set0.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">OEM White label your odoo</h2>
|
||||||
|
<h4 class="oe_slogan">Replaces "Odoo" in Windows title, Deletes Odoo label in footer, Customize all odoo link to my link</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set1.jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Mobile Enhance. Add menu navbar setup for top or bottom. navigator footer support.</h2>
|
||||||
|
<h4 class="oe_slogan">Easy set navbar on bottom or top.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setnav.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">odoo SECURITY AND BOOST</h2>
|
||||||
|
<h4 class="oe_slogan">Eonly Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.stop subscribe and follow. This to make odoo speed up.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setboost.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Add quick operation for odoo modules. </h2>
|
||||||
|
<h4 class="oe_slogan">Refresh translate. Upgrade, Uninstall, help on topic</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule1.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Easy mass module operation</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule2.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Easy Export translate follow your language</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule3.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8"> Add Draggable and sizeable Dialog enable.</h2>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setdialog.gif"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Show/Hide Author and Website in Apps Dashboard</h2>
|
||||||
|
</div>
|
||||||
|
<p>Before</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set18-1.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<p>After uncheck "Show Author in Apps Dashboard"</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set18-2.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Show quick upgrade in app dashboard, click to show module info not go to odoo.com</h2>
|
||||||
|
</div>
|
||||||
|
<p>No more redirect to odoo.com</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set20.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Setup more flags: just rename the flag pic to locale code of the country</h2>
|
||||||
|
<p>You can find the pictures in "\app-odoo\app_odoo_customize\static\src\img\flags"</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set2.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Customize Extra enterprise Module Url(eg. Enterprise).</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set21.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Quick Delete test Data.</h2>
|
||||||
|
<p>You can quickly delete all the test data in Apps: Sales/POS/Purchase/MRP/Inventory/Accounting/Message/Workflow etc.</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set3.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Multi-language Support..</h2>
|
||||||
|
<h4 class="oe_slogan"> </h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="cnreadme.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 class="mt8">
|
||||||
|
Also you can
|
||||||
|
<a href="https://www.odoo.com/apps/modules/browse?author=odooai.cn" target="_blank">
|
||||||
|
get more powerful odoo apps from us.
|
||||||
|
</a>. like [superbar widget]
|
||||||
|
</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<a href="https://www.odoo.com/apps/modules/browse?author=odooai.cn" target="_blank">
|
||||||
|
<img oe_demo oe_screenshot img img-fluid src="https://apps.odoocdn.com/apps/assets/15.0/app_web_superbar/superbar.gif"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||||
|
<p>This moduld allows user to quickly customize and debranding Odoo. Quick debug, Language Switcher,
|
||||||
|
Online Documentation Access,Quick Data Clear. </p>
|
||||||
|
<p class="mb16">Support odoo 16,15,14,13, 12, 11, 10, 9. Including community and enterprise version. </p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container app">
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="row">
|
||||||
|
<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.09.13</h3>
|
||||||
|
<p>UI enhance. follow odoo16 setup UI.</p>
|
||||||
|
<p>46. Add Help documentation anywhere. easy get help for any odoo operation or action.</p>
|
||||||
|
<p>45. Add addons path info to module.</p>
|
||||||
|
<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>
|
||||||
|
<h3>Update: v16.23.02.17</h3>
|
||||||
|
<p>Fix odoo Debug and Debug Assets.</p>
|
||||||
|
<h3>Update: v16.23.02.06</h3>
|
||||||
|
<p>Fix odoo ribbon.</p>
|
||||||
|
<p>Fix odoo translate update.</p>
|
||||||
|
<h3>Update: v16.22.10.21</h3>
|
||||||
|
<p>Add odoo16 support.</p>
|
||||||
|
<h3>Update: v15.21.10.21</h3>
|
||||||
|
<p>odoo15 remove data optimization. Easy and fast remove big data.</p>
|
||||||
|
<h3>Update: v15.22.03.15</h3>
|
||||||
|
<p>Add odoo15 supported.</p>
|
||||||
|
<h3>Update: v13.21.08.04</h3>
|
||||||
|
<p>39. Only erp manager can see debug menu..</p>
|
||||||
|
<p>38. Add Draggable Dialog enable.</p>
|
||||||
|
<p>37. Easy noupdate manage for External Identifiers(xml_id).</p>
|
||||||
|
<h3>Update: v14.21.03.31</h3>
|
||||||
|
<p>Account date reset, and account chart reset support multi company reset.</p>
|
||||||
|
<h3>Update: v14.21.03.30</h3>
|
||||||
|
<p>38. Add Draggable Dialog enable.</p>
|
||||||
|
<p>37. Easy noupdate manage for External Identifiers(xml_id).</p>
|
||||||
|
<h3>Update: v14.21.03.03</h3>
|
||||||
|
<p>Fixed odoo Title.</p>
|
||||||
|
<h3>Update: v14.20.12.29</h3>
|
||||||
|
<p>36. Add refresh translate for multi module.</p>
|
||||||
|
<h3>Update: v14.20.08.29</h3>
|
||||||
|
<p>35. Add odoo 14 Support.</p>
|
||||||
|
<p>34. Add Remove all quality data(for odoo Enterprise)</p>
|
||||||
|
<p>33. Add Demo Ribbon Setting.</p>
|
||||||
|
<p>32. Fix odoo bug of complete name bug of product category and stock location.</p>
|
||||||
|
<h3>Update: v13.20.08.29</h3>
|
||||||
|
<p>36. Add odoo 14 support.</p>
|
||||||
|
<h3>Update: v13.20.04.12</h3>
|
||||||
|
<p>31. Show or hide odoo Referral in the top menu.</p>
|
||||||
|
<h3>Update: v13.20.03.23</h3>
|
||||||
|
<p>30. 1 key export app translate file like .po file.</p>
|
||||||
|
<h3>Update: v13.20.02.25</h3>
|
||||||
|
<p>28. Easy Menu manager.</p>
|
||||||
|
<p>29. Add Install version in App list. Add Local updatable filter in app list.</p>
|
||||||
|
<h3>Update: v13.19.10.19</h3>
|
||||||
|
<p>27. Add Odoo 13 support, all function add</p>
|
||||||
|
<h3>Update: v12.19.04.30</h3>
|
||||||
|
<p>26. Add multi uninstall modules</p>
|
||||||
|
<h3>Update: v12.19.04.18</h3>
|
||||||
|
<p>25. Add remove expense data</p>
|
||||||
|
<h3>Update: v12.19.04.17</h3>
|
||||||
|
<p>Add Customize Odoo Native Module(eg. Enterprise) Url</p>
|
||||||
|
<h3>Update: v12.19.3.15</h3>
|
||||||
|
<p>Add reset or clear website blog data</p>
|
||||||
|
<h3>Update: v12.19.3.12</h3>
|
||||||
|
<p>Optimize chinese translate and document.</p>
|
||||||
|
<p>Fix bug: Data reset.</p>
|
||||||
|
<h3>Update: v12.19.1.20</h3>
|
||||||
|
<p>Fix bug: Save config error.</p>
|
||||||
|
<h3>Update: v12.19.1.05</h3>
|
||||||
|
<p>Fix bug: If you install Muk moudle, odooapp customize would pop error like "attachment_location". Sometime you need to uninstall this app and restart odoo, install again to take affect.</p>
|
||||||
|
<h3>Update: v12.0.12.25</h3>
|
||||||
|
<p>add 22. Update online manual and developer document to odoo12.</p>
|
||||||
|
<h3>Update: v12.0.11.08</h3>
|
||||||
|
<p>Add 21. Reset Account Chart.</p>
|
||||||
|
<h3>Update: v12.0.9.30</h3>
|
||||||
|
<p>Add 20. Show quick upgrade in app dashboard</p>
|
||||||
|
<h3>Update: v12.0.9.25</h3>
|
||||||
|
<p>Now ready for Odoo 13,12, please email to me. guohuadeng@hotmail.com</p>
|
||||||
|
<h3>Update: v12.0.7.23</h3>
|
||||||
|
<p>Fix Login bug when install website.</p>
|
||||||
|
<p>Add 19. One Click to clear all data (Sometime pls click twice)</p>
|
||||||
|
<p>Add 18. Show/Hide Author and Website in Apps Dashboard</p>
|
||||||
|
<h2 class="text-primary">More Powerful addons:</h2>
|
||||||
|
<p class="">
|
||||||
|
<a class="btn btn-block btn-success mt16 mb16" href="http://www.odoo.com/apps/modules/browse?author=odooai.cn">odooai.cn Awesome Odoo Addons</a>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h3>Odoo 16, 15, 14, 12, 11, 10 Support. Community and Enterprise version support</h3>
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item">1. Deletes Odoo label in footer</li>
|
||||||
|
<li class="list-group-item">2. Replaces "Odoo" in Windows title</li>
|
||||||
|
<li class="list-group-item">3. Customize Documentation, Support, About links and title in usermenu</li>
|
||||||
|
<li class="list-group-item">4. Adds "Developer mode" link to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">5. Adds Quick Language Switcher to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">6. Adds Country flags to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">7. Adds English and Chinese user documentation access to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">8. Adds developer documentation access to the top right-hand User Menu.</li>
|
||||||
|
<li class="list-group-item">9. Customize "My odoo.com account" button</li>
|
||||||
|
<li class="list-group-item">10. Standalone setting panel, easy to setup.</li>
|
||||||
|
<li class="list-group-item">11. Provide 236 country flags.</li>
|
||||||
|
<li class="list-group-item">12. Multi-language Support.</li>
|
||||||
|
<li class="list-group-item">13. Change Powered by Odoo in login screen.(Please change '../views/app_odoo_customize_view.xml' #15)</li>
|
||||||
|
<li class="list-group-item">14. Quick delete test data in Apps: Sales/POS/Purchase/MRP/Inventory/Accounting/Project/Base Models.</li>
|
||||||
|
<li class="list-group-item">15. Reset All the Sequence to beginning of 1: SO/PO/MO/Invoice...</li>
|
||||||
|
<li class="list-group-item">16. Fix odoo reload module translation bug while enable english language</li>
|
||||||
|
<li class="list-group-item">17. Stop Odoo Auto Subscribe(Performance Improve)</li>
|
||||||
|
<li class="list-group-item">18. Show/Hide Author and Website in Apps Dashboard (odoo 11 only)</li>
|
||||||
|
<li class="list-group-item">19. One Click to clear all data (Sometime pls click twice)</li>
|
||||||
|
<li class="list-group-item">20. Show quick upgrade in app dashboard, click to show module info not go to odoo.com</li>
|
||||||
|
<li class="list-group-item">21. Can clear and reset account chart. Be cautious.</li>
|
||||||
|
<li class="list-group-item">22. Update online manual and developer document to odoo12.</li>
|
||||||
|
<li class="list-group-item">23. Add reset or clear website blog data</li>
|
||||||
|
<li class="list-group-item">24. Customize Odoo Native Module(eg. Enterprise) Url</li>
|
||||||
|
<li class="list-group-item">25. Add remove expense data</li>
|
||||||
|
<li class="list-group-item">26. Add multi uninstall modules</li>
|
||||||
|
<li class="list-group-item">27. Add odoo boost modules link.</li>
|
||||||
|
<li class="list-group-item">28. Easy Menu manager.</li>
|
||||||
|
<li class="list-group-item">29. Apps version compare. Add Install version in App list. Add Local updatable filter in app list.</li>
|
||||||
|
<li class="list-group-item">30. 1 key export app translate file like .po file.</li>
|
||||||
|
<li class="list-group-item">32. Fix odoo bug of complete name bug of product category and stock location..</li>
|
||||||
|
<li class="list-group-item">33. Add Demo Ribbon Setting.</li>
|
||||||
|
<li class="list-group-item">34. Add Remove all quality data.</li>
|
||||||
|
<li class="list-group-item">35. Fixed for odoo 14.</li>
|
||||||
|
<li class="list-group-item">36. Add refresh translate for multi module.</li>
|
||||||
|
<li class="list-group-item">37. Easy noupdate manage for External Identifiers(xml_id).</li>
|
||||||
|
<li class="list-group-item">38. Add Draggable Dialog enable.</li>
|
||||||
|
<li class="list-group-item">39. Only erp manager can see debug menu.</li>
|
||||||
|
<li class="list-group-item">40. Fix support for enterprise version.</li>
|
||||||
|
<li class="list-group-item">41. Fix odoo bug, when click Preferences menu not hide in mobile.</li>
|
||||||
|
<li class="list-group-item">42. Add menu navbar setup for top or bottom. navigator footer support.</li>
|
||||||
|
<li class="list-group-item">43. Check to only Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.</li>
|
||||||
|
<li class="list-group-item">44. Check to stop subscribe and follow. This to make odoo speed up.</li>
|
||||||
|
<li class="list-group-item">45. Add addons path info to module.</li>
|
||||||
|
<li class="list-group-item">46. Add addons path info to module.</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||||
|
<p>If you want to change the login page. </p>
|
||||||
|
<p class="mb16">Please modify the file \views\app_odoo_customize_views.xml </p>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
This module can help to white label the Odoo.
|
||||||
|
Also helpful for training and support for your odoo end-user.<br/>
|
||||||
|
The user can get the help document just by one click.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Help Document Anywhere</h2>
|
||||||
|
<h4 class="oe_slogan">Get Help Documentation on current odoo operation or topic.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp1.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">You can use you company logo for the document with you help document</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp2.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">You can set extra help doc for any addons</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp3.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Must in development mode</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp4.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">New Ai Center Support</h2>
|
||||||
|
<h4 class="oe_slogan">You can install Ai service like chatgpt and google bard and azure openai.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setai1.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Odoo Customize(Debranding Title,Language,Documentation,Quick Debug)</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="banner.gif" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Multi-language support: Chinese ready</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setcn1.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
<img src="setcn2.png" style="border:1px solid black"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style=" argin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%; ">
|
||||||
|
<h2 class='oe_mt32'>How to use: Go to Settings -> odooAi -> Customize Boost Odoo</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set0.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">OEM White label your odoo</h2>
|
||||||
|
<h4 class="oe_slogan">Replaces "Odoo" in Windows title, Deletes Odoo label in footer, Customize all odoo link to my link</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set1.jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Mobile Enhance. Add menu navbar setup for top or bottom. navigator footer support.</h2>
|
||||||
|
<h4 class="oe_slogan">Easy set navbar on bottom or top.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setnav.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">odoo SECURITY AND BOOST</h2>
|
||||||
|
<h4 class="oe_slogan">Eonly Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.stop subscribe and follow. This to make odoo speed up.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setboost.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Add quick operation for odoo modules. </h2>
|
||||||
|
<h4 class="oe_slogan">Refresh translate. Upgrade, Uninstall, help on topic</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule1.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Easy mass module operation</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule2.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Easy Export translate follow your language</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule3.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8"> Add Draggable and sizeable Dialog enable.</h2>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setdialog.gif"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Show/Hide Author and Website in Apps Dashboard</h2>
|
||||||
|
</div>
|
||||||
|
<p>Before</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set18-1.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<p>After uncheck "Show Author in Apps Dashboard"</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set18-2.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Show quick upgrade in app dashboard, click to show module info not go to odoo.com</h2>
|
||||||
|
</div>
|
||||||
|
<p>No more redirect to odoo.com</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set20.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Setup more flags: just rename the flag pic to locale code of the country</h2>
|
||||||
|
<p>You can find the pictures in "\app-odoo\app_odoo_customize\static\src\img\flags"</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set2.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Customize Extra enterprise Module Url(eg. Enterprise).</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set21.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Quick Delete test Data.</h2>
|
||||||
|
<p>You can quickly delete all the test data in Apps: Sales/POS/Purchase/MRP/Inventory/Accounting/Message/Workflow etc.</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set3.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container oe_dark">
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan">Technical Help & Support</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 pad0">
|
||||||
|
<div class="oe_mt16">
|
||||||
|
<p><h4>
|
||||||
|
For any type of technical help & support requests, Feel free to contact us</h4></p>
|
||||||
|
<a style="background: #002e5a none repeat scroll 0% 0%; color: rgb(255, 255, 255);position: relative; overflow: hidden;"
|
||||||
|
class="btn btn-warning btn-lg" rel="nofollow" href="mailto:guohuadeng@hotmail.com"><span
|
||||||
|
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
|
||||||
|
<i class="fa fa-envelope"></i> guohuadeng@hotmail.com</a>
|
||||||
|
<p><h4>
|
||||||
|
Via QQ: 300883 (App user would not get QQ or any other IM support. Only for odoo project customize.)</h4></p>
|
||||||
|
<a style="background: #002e5a none repeat scroll 0% 0%; color: rgb(255, 255, 255);position: relative; overflow: hidden;"
|
||||||
|
class="btn btn-warning btn-lg" rel="nofollow" href="mailto:300883@qq.com"><span
|
||||||
|
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
|
||||||
|
<i class="fa fa-envelope"></i> 300883@qq.com</a>
|
||||||
|
</div>
|
||||||
|
<div class="oe_mt16">
|
||||||
|
<h4>
|
||||||
|
Visit our website for more support.</h4>
|
||||||
|
<h4>https://www.odooai.cn</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</html>
|
||||||
414
app_odoo_customize/static/description/index_cn.html
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
<section class="oe_container app">
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan">odoo全面增强,oem配置,界面增强,安全增强,数据增强45项功能</h2>
|
||||||
|
<h4 class="mt8">
|
||||||
|
If you are using odoo enterprise.<br>
|
||||||
|
<a href="https://www.odoo.com/apps/modules/16.0/app_web_enterprise/" target="_blank">
|
||||||
|
Get "app_web_enterprise" for more customize
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<a class="btn btn-block btn-success mt16 mb16" target="_blank" href="https://apps.odoo.com/apps/modules/16.0/app_app_web_enterprise/">
|
||||||
|
<img src="app_web_enterprise_03.jpg" style="border:1px solid black"/>
|
||||||
|
</a>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<h4 class="mt8">
|
||||||
|
You can follow this repo on github. To get the latest update of free odoo app.
|
||||||
|
<p>https://github.com/guohuadeng/app-odoo</p>
|
||||||
|
</h4>
|
||||||
|
<h4 class="mt8">
|
||||||
|
Also you can
|
||||||
|
<a href="https://www.odoo.com/apps/modules/browse?author=odooai.cn" target="_blank">
|
||||||
|
get more powerful odoo apps from us.
|
||||||
|
</a>. like [superbar widget]
|
||||||
|
</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<a href="https://www.odoo.com/apps/modules/browse?author=odooai.cn" target="_blank">
|
||||||
|
<img oe_demo oe_screenshot img img-fluid src="https://apps.odoocdn.com/apps/assets/15.0/app_web_superbar/superbar.gif"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||||
|
<p>This moduld allows user to quickly customize and debranding Odoo. Quick debug, Language Switcher,
|
||||||
|
Online Documentation Access,Quick Data Clear. </p>
|
||||||
|
<p class="mb16">Support odoo 16,15,14,13, 12, 11, 10, 9. Including community and enterprise version. </p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||||
|
<p>If you want to change the login page. </p>
|
||||||
|
<p class="mb16">Please modify the file \views\app_odoo_customize_views.xml </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container app">
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan">这是一个长期更新维护的odoo应用模块</h2>
|
||||||
|
<div class="oe_demo" style=" margin: 30px auto 0; padding: 0 15px 0 0; border:none; width: 96%;">
|
||||||
|
<h3>Update: v16.23.08.24</h3>
|
||||||
|
<p>应用界面升级,整合进odoo通用设置</p>
|
||||||
|
<p>46. 增加快速帮助文档,可以在任意操作中获取相关的 odoo 帮助.</p>
|
||||||
|
<p>45. 为应用模块增加模块路径信息</p>
|
||||||
|
<h3>Update: v16.23.08.15</h3>
|
||||||
|
<p>44. 可配置停用自动用户订阅功能,这会提速odoo,减少资源消耗</p>
|
||||||
|
<p>43. 可设置只允许管理员进入开发者模式,不可在url中直接debut=1来调试</p>
|
||||||
|
<h3>Update: v16.23.07.25</h3>
|
||||||
|
<p>42. 可设置导航栏在上方还是下方,分开桌面与移动端.</p>
|
||||||
|
<h3>Update: v16.23.07.14</h3>
|
||||||
|
<p>41. 修正odoo原生移动端菜单bug,点击个人设置时,原菜单不隐藏等</p>
|
||||||
|
<h3>Update: v16.23.05.04</h3>
|
||||||
|
<p>Fix bug in mobile view in popup menu.</p>
|
||||||
|
<h3>Update: v16.23.02.17</h3>
|
||||||
|
<p>Fix odoo Debug and Debug Assets.</p>
|
||||||
|
<h3>Update: v16.23.02.06</h3>
|
||||||
|
<p>Fix odoo ribbon.</p>
|
||||||
|
<p>Fix odoo translate update.</p>
|
||||||
|
<h3>Update: v16.22.10.21</h3>
|
||||||
|
<p>Add odoo16 support.</p>
|
||||||
|
<h3>Update: v15.21.10.21</h3>
|
||||||
|
<p>odoo15 remove data optimization. Easy and fast remove big data.</p>
|
||||||
|
<h3>Update: v15.22.03.15</h3>
|
||||||
|
<p>Add odoo15 supported.</p>
|
||||||
|
<h3>Update: v13.21.08.04</h3>
|
||||||
|
<p>40. 增强对企业版的支持</p>
|
||||||
|
<p>39. 只有系统管理员可以操作快速debug</p>
|
||||||
|
<p>38. 对话框可拖拽,可缩放,自动大屏优化</p>
|
||||||
|
<p>37. noupdate字段的快速管理,主要针对 xml_id.</p>
|
||||||
|
<h3>Update: v14.21.03.31</h3>
|
||||||
|
<p>Account date reset, and account chart reset support multi company reset.</p>
|
||||||
|
<h3>Update: v14.21.03.03</h3>
|
||||||
|
<p>Fixed odoo Title.</p>
|
||||||
|
<h3>Update: v14.20.12.29</h3>
|
||||||
|
<p>36. 可为多个模块强制更新翻译</p>
|
||||||
|
<h3>Update: v14.20.08.29</h3>
|
||||||
|
<p>35. 优化至odoo14适用</p>
|
||||||
|
<p>34. Add Remove all quality data(for odoo Enterprise)</p>
|
||||||
|
<p>33. Add Demo Ribbon Setting.</p>
|
||||||
|
<p>32. Fix odoo bug of complete name bug of product category and stock location.</p>
|
||||||
|
<h3>Update: v13.20.08.29</h3>
|
||||||
|
<p>36. Add odoo 14 support.</p>
|
||||||
|
<h3>Update: v13.20.04.12</h3>
|
||||||
|
<p>31. Show or hide odoo Referral in the top menu.</p>
|
||||||
|
<h3>Update: v13.20.03.23</h3>
|
||||||
|
<p>30. 1 key export app translate file like .po file.</p>
|
||||||
|
<h3>Update: v13.20.02.25</h3>
|
||||||
|
<p>28. Easy Menu manager.</p>
|
||||||
|
<p>29. Add Install version in App list. Add Local updatable filter in app list.</p>
|
||||||
|
<h3>Update: v13.19.10.19</h3>
|
||||||
|
<p>27. Add Odoo 13 support, all function add</p>
|
||||||
|
<h3>Update: v12.19.04.30</h3>
|
||||||
|
<p>26. Add multi uninstall modules</p>
|
||||||
|
<h3>Update: v12.19.04.18</h3>
|
||||||
|
<p>25. Add remove expense data</p>
|
||||||
|
<h3>Update: v12.19.04.17</h3>
|
||||||
|
<p>Add Customize Odoo Native Module(eg. Enterprise) Url</p>
|
||||||
|
<h3>Update: v12.19.3.15</h3>
|
||||||
|
<p>Add reset or clear website blog data</p>
|
||||||
|
<h3>Update: v12.19.3.12</h3>
|
||||||
|
<p>Optimize chinese translate and document.</p>
|
||||||
|
<p>Fix bug: Data reset.</p>
|
||||||
|
<h3>Update: v12.19.1.20</h3>
|
||||||
|
<p>Fix bug: Save config error.</p>
|
||||||
|
<h3>Update: v12.19.1.05</h3>
|
||||||
|
<p>Fix bug: If you install Muk moudle, odooapp customize would pop error like "attachment_location". Sometime you need to uninstall this app and restart odoo, install again to take affect.</p>
|
||||||
|
<h3>Update: v12.0.12.25</h3>
|
||||||
|
<p>add 22. Update online manual and developer document to odoo12.</p>
|
||||||
|
<h3>Update: v12.0.11.08</h3>
|
||||||
|
<p>Add 21. Reset Account Chart.</p>
|
||||||
|
<h3>Update: v12.0.9.30</h3>
|
||||||
|
<p>Add 20. Show quick upgrade in app dashboard</p>
|
||||||
|
<h3>Update: v12.0.9.25</h3>
|
||||||
|
<p>Now ready for Odoo 13,12, please email to me. guohuadeng@hotmail.com</p>
|
||||||
|
<h3>Update: v12.0.7.23</h3>
|
||||||
|
<p>Fix Login bug when install website.</p>
|
||||||
|
<p>Add 19. One Click to clear all data (Sometime pls click twice)</p>
|
||||||
|
<p>Add 18. Show/Hide Author and Website in Apps Dashboard</p>
|
||||||
|
<h2 class="text-primary">More Powerful addons:</h2>
|
||||||
|
<p class="">
|
||||||
|
<a class="btn btn-block btn-success mt16 mb16" href="http://www.odoo.com/apps/modules/browse?author=odooai.cn">odooai.cn Awesome Odoo
|
||||||
|
Addons</a>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h3>支持odoo 16,15,14,13,12, 11, 10, 9 版本,社区版企业版通用</h3>
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class='list-group-item'>1. 删除菜单导航页脚的 Odoo 标签</li>
|
||||||
|
<li class='list-group-item'>2. 将弹出窗口中 "Odoo" 设置为自定义名称</li>
|
||||||
|
<li class='list-group-item'>3. 自定义用户菜单中的 Documentation, Support, About 的链接</li>
|
||||||
|
<li class='list-group-item'>4. 在用户菜单中增加快速切换开发模式</li>
|
||||||
|
<li class='list-group-item'>5. 在用户菜单中增加快速切换多国语言</li>
|
||||||
|
<li class='list-group-item'>6. 对语言菜单进行美化,设置国旗图标</li>
|
||||||
|
<li class='list-group-item'>7. 在用户菜单中增加中/英文用户手册,可以不用翻墙加速了</li>
|
||||||
|
<li class='list-group-item'>8. 在用户菜单中增加开发者手册,含python教程,jquery参考,Jinja2模板,PostgresSQL参考</li>
|
||||||
|
<li class='list-group-item'>9. 在用户菜单中自定义"My odoo.com account"</li>
|
||||||
|
<li class='list-group-item'>10. 单独设置面板,每个选项都可以自定义</li>
|
||||||
|
<li class='list-group-item'>11. 提供236个国家的国旗文件(部份需要自行设置文件名)</li>
|
||||||
|
<li class='list-group-item'>12. 多语言版本</li>
|
||||||
|
<li class='list-group-item'>13. 自定义登陆界面中的 Powered by Odoo</li>
|
||||||
|
<li class='list-group-item'>14. 快速删除测试数据,支持模块包括:销售/POS门店/采购/生产/库存/会计/项目/消息与工作流等.</li>
|
||||||
|
<li class='list-group-item'>15. 将各类单据的序号重置,从1开始,包括:SO/PO/MO/Invoice 等</li>
|
||||||
|
<li class='list-group-item'>16. 修复odoo启用英文后模块不显示中文的Bug</li>
|
||||||
|
<li class='list-group-item'>17. 可停用odoo自动订阅功能,避免“同样对象关注2次”bug,同时提升性能</li>
|
||||||
|
<li class='list-group-item'>18. 显示/隐藏应用的作者和网站-在应用安装面板中</li>
|
||||||
|
<li class='list-group-item'>19. 一键清除所有数据(视当前数据情况,有时需点击2次)</li>
|
||||||
|
<li class='list-group-item'>20. 在应用面板显示快速升级按键,点击时不会导航至 odoo.com</li>
|
||||||
|
<li class='list-group-item'>21. 清除并重置会计科目表</li>
|
||||||
|
<li class='list-group-item'>22. 全新升级将odoo12用户及开发手册导航至国内网站,或者自己定义的网站</li>
|
||||||
|
<li class='list-group-item'>23. 增加清除网站数据功能</li>
|
||||||
|
<li class='list-group-item'>24. 自定义 odoo 原生模块跳转的url(比如企业版模块)</li>
|
||||||
|
<li class='list-group-item'>25. 增加删除费用报销数据功能</li>
|
||||||
|
<li class='list-group-item'>26. 增加批量卸载模块功能</li>
|
||||||
|
<li class='list-group-item'>27. 增加odoo加速功能</li>
|
||||||
|
<li class='list-group-item'>28. 快速管理顶级菜单</li>
|
||||||
|
<li class='list-group-item'>29. App版本比较,快速查看可本地更新的模块</li>
|
||||||
|
<li class='list-group-item'>30. 一键导出翻译文件 po</li>
|
||||||
|
<li class='list-group-item'>31. 显示或去除 odoo 推荐</li>
|
||||||
|
<li class='list-group-item'>32. 增加修复品类及区位名的操作</li>
|
||||||
|
<li class='list-group-item'>33. 增加 Demo 的显示设置</li>
|
||||||
|
<li class='list-group-item'>34. 增加清除质检数据</li>
|
||||||
|
<li class='list-group-item'>35. 优化至odoo14适用</li>
|
||||||
|
<li class='list-group-item'>36. 可为多个模块强制更新翻译</li>
|
||||||
|
<li class='list-group-item'>37. noupdate字段的快速管理,主要针对 xml_id</li>
|
||||||
|
<li class='list-group-item'>38. 对话框可拖拽,可缩放,自动大屏优化</li>
|
||||||
|
<li class='list-group-item'>39. 只有系统管理员可以操作快速debug</li>
|
||||||
|
<li class='list-group-item'>40. 增强对企业版的支持</li>
|
||||||
|
<li class='list-group-item'>41. 修正odoo原生移动端菜单bug,点击个人设置时,原菜单不隐藏等</li>
|
||||||
|
<li class='list-group-item'>42. 可设置导航栏在上方还是下方,分开桌面与移动端.</li>
|
||||||
|
<li class='list-group-item'>43. 可设置只允许管理员进入开发者模式,不可在url中直接debut=1来调试</li>
|
||||||
|
<li class='list-group-item'>44. 可配置停用自动用户订阅功能,这会提速odoo,减少资源消耗</li>
|
||||||
|
<li class='list-group-item'>45. 为应用模块增加模块路径信息</li>
|
||||||
|
<li class='list-group-item'>46. 增加快速帮助文档,可以在任意操作中获取相关的 odoo 帮助.</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
<p>此模块用于 OEM 你的 odoo,增加自己公司的 Logo 相关信息及版权相关信息。
|
||||||
|
<br/>
|
||||||
|
便于快速开发、快速培训、可速查找odoo帮助文档。也可以方便的进行odoo数据清理与重置。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">odoo系统内快速帮助</h2>
|
||||||
|
<h4 class="oe_slogan">增加快速帮助文档,可以在任意操作中获取相关的 odoo 帮助.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp1.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">自动设置文章Logo为公司Logo</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp2.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">可手动设置模块帮助链接</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp3.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">注意必须在开发者模式</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="sethelp4.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Ai服务中心支持</h2>
|
||||||
|
<h4 class="oe_slogan">你可以安装最新的 ChatGPT 或者 微软、谷歌等Ai.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setai1.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Odoo 快速自定义,OEM改造,快速Debug、语言切换等全面增强</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="banner.gif" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>多语言支持,中文已处理</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setcn1.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
<img src="setcn2.png" style="border:1px solid black"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style=" argin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%; ">
|
||||||
|
<h2 class='oe_mt32'>如何使用: 转到 设置 -> odooAi -> 定制与增强</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set0.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">OEM White label your odoo</h2>
|
||||||
|
<h4 class="oe_slogan">Replaces "Odoo" in Windows title, Deletes Odoo label in footer, Customize all odoo link to my link</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set1.jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Mobile Enhance. Add menu navbar setup for top or bottom. navigator footer support.</h2>
|
||||||
|
<h4 class="oe_slogan">Easy set navbar on bottom or top.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setnav.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">odoo SECURITY AND BOOST</h2>
|
||||||
|
<h4 class="oe_slogan">Eonly Debug / Debug Assets for Odoo Admin. Deny debug from url for other user.stop subscribe and follow. This to make odoo speed up.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setboost.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Add quick operation for odoo modules. </h2>
|
||||||
|
<h4 class="oe_slogan">Refresh translate. Upgrade, Uninstall, help on topic</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule1.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Easy mass module operation</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule2.png"/>
|
||||||
|
</div>
|
||||||
|
<h4 class="oe_slogan">Easy Export translate follow your language</h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setmodule3.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8"> Add Draggable and sizeable Dialog enable.</h2>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="setdialog.gif"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Show/Hide Author and Website in Apps Dashboard</h2>
|
||||||
|
</div>
|
||||||
|
<p>Before</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set18-1.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<p>After uncheck "Show Author in Apps Dashboard"</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set18-2.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Show quick upgrade in app dashboard, click to show module info not go to odoo.com</h2>
|
||||||
|
</div>
|
||||||
|
<p>No more redirect to odoo.com</p>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set20.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Setup more flags: just rename the flag pic to locale code of the country</h2>
|
||||||
|
<p>You can find the pictures in "\app-odoo\app_odoo_customize\static\src\img\flags"</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set2.png" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Customize Extra enterprise Module Url(eg. Enterprise).</h2>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set21.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="oe_row oe_spaced" style="max-width: 95%;">
|
||||||
|
<div class="oe_demo"
|
||||||
|
style="margin: 20px auto; padding: 0 15px 0 0; border:none; border-top:solid 1px #dedede; width: 96%;">
|
||||||
|
<h2 class='oe_mt32'>Quick Delete test Data.</h2>
|
||||||
|
<p>You can quickly delete all the test data in Apps: Sales/POS/Purchase/MRP/Inventory/Accounting/Message/Workflow etc.</p>
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="set3.jpg" style="border:1px solid black"/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h2 class="bg-warning text-center pt8 pb8">Multi-language Support..</h2>
|
||||||
|
<h4 class="oe_slogan"> </h4>
|
||||||
|
<div class="oe_demo oe_screenshot img img-fluid">
|
||||||
|
<img src="cnreadme.png"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="container oe_dark">
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<div class="row">
|
||||||
|
<h2 class="oe_slogan">Technical Help & Support</h2>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12 pad0">
|
||||||
|
<div class="oe_mt16">
|
||||||
|
<p><h4>
|
||||||
|
For any type of technical help & support requests, Feel free to contact us</h4></p>
|
||||||
|
<a style="background: #002e5a none repeat scroll 0% 0%; color: rgb(255, 255, 255);position: relative; overflow: hidden;"
|
||||||
|
class="btn btn-warning btn-lg" rel="nofollow" href="mailto:guohuadeng@hotmail.com"><span
|
||||||
|
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
|
||||||
|
<i class="fa fa-envelope"></i> guohuadeng@hotmail.com</a>
|
||||||
|
<p><h4>
|
||||||
|
Via QQ: 300883 (App user would not get QQ or any other IM support. Only for odoo project customize.)</h4></p>
|
||||||
|
<a style="background: #002e5a none repeat scroll 0% 0%; color: rgb(255, 255, 255);position: relative; overflow: hidden;"
|
||||||
|
class="btn btn-warning btn-lg" rel="nofollow" href="mailto:300883@qq.com"><span
|
||||||
|
style="height: 354px; width: 354px; top: -147.433px; left: -6.93335px;" class="o_ripple"></span>
|
||||||
|
<i class="fa fa-envelope"></i> 300883@qq.com</a>
|
||||||
|
</div>
|
||||||
|
<div class="oe_mt16">
|
||||||
|
<h4>
|
||||||
|
Visit our website for more support.</h4>
|
||||||
|
<h4>https://www.odooai.cn</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
BIN
app_odoo_customize/static/description/set0.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
app_odoo_customize/static/description/set1.jpg
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
app_odoo_customize/static/description/set12.png
Normal file
|
After Width: | Height: | Size: 331 KiB |
BIN
app_odoo_customize/static/description/set18-1.jpg
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
app_odoo_customize/static/description/set18-2.jpg
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
app_odoo_customize/static/description/set19.jpg
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
app_odoo_customize/static/description/set2.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
app_odoo_customize/static/description/set20.jpg
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
app_odoo_customize/static/description/set21.jpg
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
app_odoo_customize/static/description/set3.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
app_odoo_customize/static/description/setai1.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
app_odoo_customize/static/description/setboost.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
app_odoo_customize/static/description/setcn1.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
app_odoo_customize/static/description/setcn2.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
app_odoo_customize/static/description/setdialog.gif
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
app_odoo_customize/static/description/sethelp1.png
Normal file
|
After Width: | Height: | Size: 371 KiB |
BIN
app_odoo_customize/static/description/sethelp2.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
app_odoo_customize/static/description/sethelp3.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
app_odoo_customize/static/description/sethelp4.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
app_odoo_customize/static/description/setmodule1.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
app_odoo_customize/static/description/setmodule2.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
app_odoo_customize/static/description/setmodule3.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
app_odoo_customize/static/description/setnav.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,53 @@
|
|||||||
|
/** @odoo-module **/
|
||||||
|
|
||||||
|
import { Dropdown } from "@web/core/dropdown/dropdown";
|
||||||
|
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
|
||||||
|
import { useService } from "@web/core/utils/hooks";
|
||||||
|
import { registry } from "@web/core/registry";
|
||||||
|
import { browser } from "@web/core/browser/browser";
|
||||||
|
import { symmetricalDifference } from "@web/core/utils/arrays";
|
||||||
|
|
||||||
|
import { Component, useState } from "@odoo/owl";
|
||||||
|
|
||||||
|
export class SwitchLangMenu extends Component {
|
||||||
|
setup() {
|
||||||
|
this.LangService = useService("Lang");
|
||||||
|
this.currentLang = this.LangService.currentLang;
|
||||||
|
this.state = useState({ langToSet: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
setLang(LangId) {
|
||||||
|
this.state.langToSet = symmetricalDifference(this.state.langToSet, [
|
||||||
|
LangId,
|
||||||
|
]);
|
||||||
|
browser.clearTimeout(this.toggleTimer);
|
||||||
|
this.toggleTimer = browser.setTimeout(() => {
|
||||||
|
this.LangService.set2Lang("toggle", ...this.state.langToSet);
|
||||||
|
}, this.constructor.toggleDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
logIntoLang(LangId) {
|
||||||
|
browser.clearTimeout(this.toggleTimer);
|
||||||
|
this.LangService.set2Lang("loginto", LangId);
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedCompanies() {
|
||||||
|
return symmetricalDifference(
|
||||||
|
this.LangService.allowedLangIds,
|
||||||
|
this.state.langToSet
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwitchLangMenu.template = "web.SwitchLangMenu";
|
||||||
|
SwitchLangMenu.components = { Dropdown, DropdownItem };
|
||||||
|
SwitchLangMenu.toggleDelay = 1000;
|
||||||
|
|
||||||
|
export const systrayItem = {
|
||||||
|
Component: SwitchLangMenu,
|
||||||
|
isDisplayed(env) {
|
||||||
|
const { availableCompanies } = env.services.Lang;
|
||||||
|
return Object.keys(availableCompanies).length > 1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
registry.category("systray").add("SwitchLangMenu", systrayItem, { sequence: 1 });
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates xml:space="preserve">
|
||||||
|
|
||||||
|
<t t-name="web.SwitchLangMenu" owl="1">
|
||||||
|
<Dropdown class="'o_switch_Lang_menu d-none d-md-block'" position="'bottom-end'">
|
||||||
|
<t t-set-slot="toggler">
|
||||||
|
<i class="fa fa-building d-lg-none"/>
|
||||||
|
<span class="oe_topbar_name d-none d-lg-block" t-esc="currentLang.name"/>
|
||||||
|
</t>
|
||||||
|
<t t-foreach="Object.values(LangService.availableCompanies).sort((c1, c2) => c1.sequence - c2.sequence)" t-as="Lang" t-key="Lang.id">
|
||||||
|
<t t-call="web.SwitchLangItem">
|
||||||
|
<t t-set="Lang" t-value="Lang" />
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</Dropdown>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
|
||||||
|
<t t-name="web.SwitchLangItem" owl="1">
|
||||||
|
<DropdownItem class="'p-0 bg-white'">
|
||||||
|
<t t-set="isLangSelected" t-value="selectedCompanies.includes(Lang.id)"/>
|
||||||
|
<t t-set="isCurrent" t-value="Lang.id === LangService.currentLang.id"/>
|
||||||
|
<div class="d-flex" data-menu="Lang" t-att-data-Lang-id="Lang.id">
|
||||||
|
<div
|
||||||
|
role="menuitemcheckbox"
|
||||||
|
t-att-aria-checked="isLangSelected ? 'true' : 'false'"
|
||||||
|
t-att-aria-label="Lang.name"
|
||||||
|
t-att-title="(isLangSelected ? 'Hide ' : 'Show ') + Lang.name + ' content.'"
|
||||||
|
tabindex="0"
|
||||||
|
class="border-end toggle_Lang"
|
||||||
|
t-attf-class="{{isCurrent ? 'border-primary' : ''}}"
|
||||||
|
t-on-click.stop="() => this.setLang(Lang.id)">
|
||||||
|
|
||||||
|
<span class="btn btn-light border-0 p-2">
|
||||||
|
<i class="fa fa-fw py-2" t-att-class="isLangSelected ? 'fa-check-square text-primary' : 'fa-square-o'"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
t-att-aria-pressed="isCurrent ? 'true' : 'false'"
|
||||||
|
t-att-aria-label="'Switch to ' + Lang.name "
|
||||||
|
t-att-title="'Switch to ' + Lang.name "
|
||||||
|
tabindex="0"
|
||||||
|
class="d-flex flex-grow-1 align-items-center py-0 log_into ps-2"
|
||||||
|
t-att-class="isCurrent ? 'alert-primary ms-1 me-2' : 'btn btn-light fw-normal border-0'"
|
||||||
|
t-on-click="() => this.logIntoLang(Lang.id)">
|
||||||
|
|
||||||
|
<span
|
||||||
|
class='Lang_label pe-3'
|
||||||
|
t-att-class="isCurrent ? 'text-900 fw-bold' : 'ms-1'">
|
||||||
|
<t t-esc="Lang.name"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DropdownItem>
|
||||||
|
</t>
|
||||||
|
|
||||||
|
</templates>
|
||||||
BIN
app_odoo_customize/static/src/img/flags/ar_SY.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app_odoo_customize/static/src/img/flags/bg.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app_odoo_customize/static/src/img/flags/bs_BA.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
app_odoo_customize/static/src/img/flags/de_DE.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app_odoo_customize/static/src/img/flags/en_AU.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
app_odoo_customize/static/src/img/flags/en_GB.png
Normal file
|
After Width: | Height: | Size: 54 KiB |