int app-odoo 17, paid

This commit is contained in:
Ivan Office
2023-09-30 10:58:00 +08:00
parent e346c360a2
commit 72c740dd76
355 changed files with 14784 additions and 0 deletions

7
app_common/__init__.py Normal file
View 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

View 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 Deng300883@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,
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import main

View 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
View 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
View 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 "查看"

View File

@@ -0,0 +1 @@
VERSION = (0, 10, 0)

File diff suppressed because it is too large Load Diff

View 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

View 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()

View File

@@ -0,0 +1,3 @@
VERSION = (2, 2, 0)
from .parsers import parse

View 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))

View 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)

View 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))

View 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

View 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
View 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

View 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()

View 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

View 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)

View 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):

View 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)

View 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

View 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)

View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-

445
app_common/rng/common.rng Normal file
View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

View File

View 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>

View File

View 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

View 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 帮助.
""",
}

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# todo: website 有bug oauth
from . import controllers

View 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

View 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&amp;model=digest.digest&amp;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&amp;model=digest.digest&amp;id=${object.id}" target="_blank" style="color: #888888;">Unsubscribe</a>
</strong>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
]]></field>
</record>
</odoo>

View 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>

View 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>

View 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>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0"?>
<odoo>
<data noupdate="1">
<function model="res.config.settings" name="set_module_url"/>
</data>
</odoo>

View 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)

View 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 &amp;copy;"
msgstr "版权所有 &amp;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"

View 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

View 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"

View 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

View 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)],
}

View 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

View 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

View 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)

View 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)

View 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:

View 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"
),
}

View 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:

View 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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ir_config_parameter_system ir_config_parameter_system base.model_ir_config_parameter base.group_user 1 0 0 0
3 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

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -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 });

View File

@@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Some files were not shown because too many files have changed in this diff Show More