update paid
8
app_base_chinese/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from .hooks import pre_init_hook
|
||||||
|
from .hooks import post_init_hook
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from . import ir
|
||||||
|
from . import res
|
||||||
99
app_base_chinese/__manifest__.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 2018-11-05
|
||||||
|
# author: 广州尚鹏,https://www.sunpop.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of Sunpop
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
# Odoo在线中文用户手册(长期更新)
|
||||||
|
# https://www.sunpop.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.sunpop.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.sunpop.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': "App base chinese,中国化基本模块增强",
|
||||||
|
'version': '15.22.07.20',
|
||||||
|
'author': 'Sunpop.cn',
|
||||||
|
'category': 'Base',
|
||||||
|
'website': 'https://www.sunpop.cn',
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'sequence': 2,
|
||||||
|
'price': 0,
|
||||||
|
'currency': 'EUR',
|
||||||
|
'summary': """
|
||||||
|
Chinese enhance. Out of the box use in china.
|
||||||
|
Set all chinese default value.
|
||||||
|
Add quick set of english name.
|
||||||
|
Default country, timezone, currency, partner...
|
||||||
|
""",
|
||||||
|
'description': """
|
||||||
|
|
||||||
|
odoo Chinese Enhance. 中国化增强-基础
|
||||||
|
1. 中文默认值,如国家、时区、货币等。处理模块 base, product.
|
||||||
|
2. 客户加简称,地址显示中文化,客户编码显示优先
|
||||||
|
3. 客户地址显示增加手机号与电话号码
|
||||||
|
4. 货币处理,增加排序显示
|
||||||
|
5. 用户名支持翻译(可能会增加复杂度,后续看)
|
||||||
|
6. 修正品类的列表及m2o字段中不显示中文目录名的Bug
|
||||||
|
7. 修正仓库位置的列表及m2o字段中不显示中文目录名的Bug
|
||||||
|
8. 超级用户改时区为 中国
|
||||||
|
9. 时间格式年月日为 2019-12-30,时间为 22:10
|
||||||
|
10. 国家增加排序,中国排第一
|
||||||
|
11. 收款相关显示中国习惯
|
||||||
|
12. 翻译导出默认中文,默认po
|
||||||
|
13. 在 base 模型增加 name_en_US 字段,赋值后同时改翻译值
|
||||||
|
14. 常用小数精度调整
|
||||||
|
15. 销售团队改为中国
|
||||||
|
21. todo:中文演示数据(只有demo模式才加载)
|
||||||
|
""",
|
||||||
|
'pre_init_hook': 'pre_init_hook',
|
||||||
|
'post_init_hook': 'post_init_hook',
|
||||||
|
'depends': [
|
||||||
|
'app_base',
|
||||||
|
'account',
|
||||||
|
'sales_team',
|
||||||
|
'stock',
|
||||||
|
],
|
||||||
|
'images': ['static/description/banner.jpg'],
|
||||||
|
'data': [
|
||||||
|
'views/res_partner_views.xml',
|
||||||
|
'views/res_currency_views.xml',
|
||||||
|
'views/sale_order_views.xml',
|
||||||
|
'views/account_move_views.xml',
|
||||||
|
'views/ir_default_views.xml',
|
||||||
|
# todo: set assets
|
||||||
|
# 'views/templates.xml',
|
||||||
|
'wizard/sale_make_invoice_advance_views.xml',
|
||||||
|
'data/ir_default_data.xml',
|
||||||
|
'data/base_data.xml',
|
||||||
|
'data/decimal_precision_data.xml',
|
||||||
|
'data/res_country_data.xml',
|
||||||
|
'data/res_currency_data.xml',
|
||||||
|
'data/res_lang_data.xml',
|
||||||
|
'data/product_data.xml',
|
||||||
|
'data/product_pricelist_data.xml',
|
||||||
|
'data/stock_location_data.xml',
|
||||||
|
'data/sales_team_data.xml',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'test': [
|
||||||
|
],
|
||||||
|
'css': [
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
'static/src/xml/*.xml',
|
||||||
|
],
|
||||||
|
'js': [
|
||||||
|
],
|
||||||
|
'post_init_hook': 'post_init_hook',
|
||||||
|
'installable': True,
|
||||||
|
'application': True,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
||||||
1
app_base_chinese/controllers/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
19
app_base_chinese/data/base_data.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<function name="install_lang" model="res.lang"/>
|
||||||
|
|
||||||
|
<record id="base.main_partner" model="res.partner" context="{'default_is_company': True}">
|
||||||
|
<field name="name">广州尚鹏</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Basic Company -->
|
||||||
|
<record id="base.main_company" model="res.company">
|
||||||
|
<field name="name">广州尚鹏</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="res.partner" id="base.partner_root">
|
||||||
|
<field name="name">超管</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
8
app_base_chinese/data/crm_team_data.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!--<record id="sales_team.team_sales_department" model="crm.team">-->
|
||||||
|
<!--<field name="name">Sales</field>-->
|
||||||
|
<!--</record>-->
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
14
app_base_chinese/data/decimal_precision_data.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="product.decimal_discount" model="decimal.precision">
|
||||||
|
<field name="digits">0</field>
|
||||||
|
</record>
|
||||||
|
<record id="product.decimal_product_uom" model="decimal.precision">
|
||||||
|
<field name="digits">2</field>
|
||||||
|
</record>
|
||||||
|
<record id="account.decimal_payment" model="decimal.precision">
|
||||||
|
<field name="digits">3</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
36
app_base_chinese/data/ir_default_data.xml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- 各种默认值 -->
|
||||||
|
<!--设置客户默认国家为中国-->
|
||||||
|
<record id="res_partner_country_id_default" model="ir.default">
|
||||||
|
<field name="field_id" eval="ref('base.field_res_partner__country_id')"/>
|
||||||
|
<field name="json_value" eval="ref('base.cn')"/>
|
||||||
|
</record>
|
||||||
|
<!--设置默认时区为中国,注意select 类型要加 ""-->
|
||||||
|
<record id="res_partner_tz_default" model="ir.default">
|
||||||
|
<field name="field_id" eval="ref('base.field_res_partner__tz')"/>
|
||||||
|
<field name="json_value">"Etc/GMT-8"</field>
|
||||||
|
</record>
|
||||||
|
<!--设置用户email在odoo内提醒-->
|
||||||
|
<record id="res_user_notify_email_default" model="ir.default">
|
||||||
|
<field name="field_id" eval="ref('mail.field_res_users__notification_type')"/>
|
||||||
|
<field name="json_value">"inbox"</field>
|
||||||
|
</record>
|
||||||
|
<!--设置产品默认是可库存产品-->
|
||||||
|
<record id="product_template_type_default" model="ir.default">
|
||||||
|
<field name="field_id" eval="ref('product.field_product_template__type')"/>
|
||||||
|
<field name="json_value">"product"</field>
|
||||||
|
</record>
|
||||||
|
<!--设置翻译导出默认中文-->
|
||||||
|
<record id="base_language_export_lang_default" model="ir.default">
|
||||||
|
<field name="field_id" eval="ref('base.field_base_language_export__lang')"/>
|
||||||
|
<field name="json_value">"zh_CN"</field>
|
||||||
|
</record>
|
||||||
|
<!--设置翻译导出默认po-->
|
||||||
|
<record id="base_language_export_format_default" model="ir.default">
|
||||||
|
<field name="field_id" eval="ref('base.field_base_language_export__format')"/>
|
||||||
|
<field name="json_value">"po"</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
14
app_base_chinese/data/product_data.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!--
|
||||||
|
Resource: product.uom.categ
|
||||||
|
-->
|
||||||
|
<!--<record id="product.product_category_all" model="product.category">-->
|
||||||
|
<!--<field name="name">全部</field>-->
|
||||||
|
<!--</record>-->
|
||||||
|
<!--<record id="product.product_category_1" model="product.category">-->
|
||||||
|
<!--<field name="name">可销售</field>-->
|
||||||
|
<!--</record>-->
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
9
app_base_chinese/data/product_pricelist_data.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!-- Price list -->
|
||||||
|
<record id="product.list0" model="product.pricelist">
|
||||||
|
<field name="currency_id" ref="base.CNY"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
9
app_base_chinese/data/res_country_data.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!-- Country -->
|
||||||
|
<record id="base.cn" model="res.country">
|
||||||
|
<field name="address_format" eval="'%(country_name)s%(state_name)s%(city)s%(street)s\n%(street2)s'"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
18
app_base_chinese/data/res_currency_data.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!-- Currencies -->
|
||||||
|
<record id="base.USD" model="res.currency">
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
</record>
|
||||||
|
<record id="base.EUR" model="res.currency">
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
</record>
|
||||||
|
<record id="base.CNY" model="res.currency">
|
||||||
|
<field name="active" eval="True"/>
|
||||||
|
<field name="currency_unit_label">元</field>
|
||||||
|
<field name="currency_subunit_label">分</field>
|
||||||
|
<field name="position">before</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
10
app_base_chinese/data/res_lang_data.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="0">
|
||||||
|
<!-- 时间格式 -->
|
||||||
|
<record id="base.lang_zh_CN" model="res.lang">
|
||||||
|
<field name="date_format">%Y-%m-%d</field>
|
||||||
|
<field name="time_format">%H:%M:%S</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
8
app_base_chinese/data/sales_team_data.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<record id="sales_team.team_sales_department" model="crm.team">
|
||||||
|
<field name="name">中国</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
7
app_base_chinese/data/stock.location.csv
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
id,name
|
||||||
|
stock.stock_location_locations_partner,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ
|
||||||
|
stock.stock_location_customers,<EFBFBD>ͻ<EFBFBD><EFBFBD><EFBFBD>λ
|
||||||
|
stock.stock_location_suppliers,<EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ
|
||||||
|
stock.stock_location_locations,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ
|
||||||
|
stock.stock_location_locations_virtual,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ
|
||||||
|
stock.stock_location_inter_wh,<EFBFBD>ڲ<EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD>λ
|
||||||
|
31
app_base_chinese/data/stock_location_data.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="stock.removal_fifo" model="product.removal">
|
||||||
|
<field name="name">先进先出(FIFO)</field>
|
||||||
|
</record>
|
||||||
|
<record id="stock.removal_lifo" model="product.removal">
|
||||||
|
<field name="name">后进先出(LIFO)</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
<data noupdate="0">
|
||||||
|
<record id="stock.stock_location_locations" model="stock.location">
|
||||||
|
<field name="name">物理区位</field>
|
||||||
|
</record>
|
||||||
|
<record id="stock.stock_location_locations_partner" model="stock.location">
|
||||||
|
<field name="name">往来单位</field>
|
||||||
|
</record>
|
||||||
|
<record id="stock.stock_location_locations_virtual" model="stock.location">
|
||||||
|
<field name="name">虚拟区位</field>
|
||||||
|
</record>
|
||||||
|
<record id="stock.stock_location_suppliers" model="stock.location">
|
||||||
|
<field name="name">供应商区位</field>
|
||||||
|
</record>
|
||||||
|
<record id="stock.stock_location_customers" model="stock.location">
|
||||||
|
<field name="name">客户区位</field>
|
||||||
|
</record>
|
||||||
|
<record id="stock.stock_location_inter_wh" model="stock.location">
|
||||||
|
<field name="name">内部中转区位</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
90
app_base_chinese/demo/base_demo.xml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record id="partner_demo" model="res.partner">
|
||||||
|
<field name="name">Demo User</field>
|
||||||
|
<field name="company_id" ref="main_company"/>
|
||||||
|
<field name="customer" eval="False"/>
|
||||||
|
<field name="email">demo@yourcompany.example.com</field>
|
||||||
|
<field name="company_name">YourCompany</field>
|
||||||
|
<field name="street">Avenue des Dessus-de-Lives, 2</field>
|
||||||
|
<field name="city">Namur (Loyers)</field>
|
||||||
|
<field name="zip">5101</field>
|
||||||
|
<field name="country_id" ref="be"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="res.partner.bank" id="bank_partner_demo">
|
||||||
|
<field name="acc_number">60-16-13 31926819</field>
|
||||||
|
<field name="acc_type">bank</field>
|
||||||
|
<field name="partner_id" ref="partner_demo" />
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="main_partner" model="res.partner">
|
||||||
|
<field name="name">YourCompany</field>
|
||||||
|
<field name="company_name">YourCompany</field>
|
||||||
|
<field name="street">1725 Slough Ave.</field>
|
||||||
|
<field name="city">Scranton</field>
|
||||||
|
<field name="zip">18540</field>
|
||||||
|
<field name='country_id' ref='base.us'/>
|
||||||
|
<field name='state_id' ref='state_us_39'/>
|
||||||
|
<field name="phone">+1 555 123 8069</field>
|
||||||
|
<field name="email">info@yourcompany.example.com</field>
|
||||||
|
<field name="website">www.example.com</field>
|
||||||
|
<field name="image" type="base64" file="base/res/res_company_logo.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="main_company" model="res.company">
|
||||||
|
<field name="name">YourCompany</field>
|
||||||
|
<field name="external_report_layout">standard</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="user_demo" model="res.users">
|
||||||
|
<field name="partner_id" ref="base.partner_demo"/>
|
||||||
|
<field name="login">demo</field>
|
||||||
|
<field name="password">demo</field>
|
||||||
|
<field name="signature" type="xml"><span>-- <br/>+Mr Demo</span></field>
|
||||||
|
<field name="company_id" ref="main_company"/>
|
||||||
|
<field name="groups_id" eval="[(6,0,[ref('base.group_user'), ref('base.group_partner_manager')])]"/>
|
||||||
|
<field name="image" type="base64" file="base/static/img/user_demo-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="res.partner" id="base.partner_root">
|
||||||
|
<field name="company_name">YourCompany</field>
|
||||||
|
<field name="street">215 Vine St</field>
|
||||||
|
<field name="city">Scranton</field>
|
||||||
|
<field name="zip">18503</field>
|
||||||
|
<field name='country_id' ref='base.us'/>
|
||||||
|
<field name='state_id' ref='state_us_39'/>
|
||||||
|
<field name="phone">+1 555-555-5555</field>
|
||||||
|
<field name="email">admin@yourcompany.example.com</field>
|
||||||
|
<field name="tz">Europe/Brussels</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/partner_root-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Portal : partner and user -->
|
||||||
|
<record id="partner_demo_portal" model="res.partner">
|
||||||
|
<field name="name">Demo Portal User</field>
|
||||||
|
<field name="email">demo.portal@yourcompany.example.com</field>
|
||||||
|
<field name="supplier" eval="False"/>
|
||||||
|
<field name="customer" eval="True"/>
|
||||||
|
<field name="city">Vivegnis</field>
|
||||||
|
<field name="zip">4683</field>
|
||||||
|
<field name="country_id" ref="base.be"/>
|
||||||
|
<field name="company_name">YourCompany</field>
|
||||||
|
<field name="street">Rue Cesar de Paepe, 43</field>
|
||||||
|
</record>
|
||||||
|
<record id="demo_user0" model="res.users" context="{'no_reset_password': True}">
|
||||||
|
<field name="partner_id" ref="partner_demo_portal"/>
|
||||||
|
<field name="login">portal</field>
|
||||||
|
<field name="password">portal</field>
|
||||||
|
<field name="signature"><![CDATA[<span>-- <br/>Mr Demo Portal</span>]]></field>
|
||||||
|
<field name="groups_id" eval="[(5,)]"/><!-- Avoid auto-including this user in any default group -->
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.group_portal" model="res.groups"><!-- Add the demo user to the portal (and therefore to the portal member group) -->
|
||||||
|
<field name="users" eval="[(4,ref('demo_user0'))]"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
738
app_base_chinese/demo/product_demo.xml
Normal file
@@ -0,0 +1,738 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record id="product_category_2" model="product.category">
|
||||||
|
<field name="parent_id" ref="product.product_category_all"/>
|
||||||
|
<field name="name">Internal</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_category_3" model="product.category">
|
||||||
|
<field name="parent_id" ref="product.product_category_1"/>
|
||||||
|
<field name="name">Services</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_category_4" model="product.category">
|
||||||
|
<field name="parent_id" ref="product.product_category_1"/>
|
||||||
|
<field name="name">Software</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_category_5" model="product.category">
|
||||||
|
<field name="parent_id" ref="product_category_1"/>
|
||||||
|
<field name="name">Physical</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="service_order_01" model="product.product">
|
||||||
|
<field name="name">Prepaid Consulting</field>
|
||||||
|
<field name="categ_id" ref="product_category_3"/>
|
||||||
|
<field name="standard_price">40</field>
|
||||||
|
<field name="list_price">90</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="uom_id" ref="product_uom_hour"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_hour"/>
|
||||||
|
<field name="description">Example of product to invoice on order.</field>
|
||||||
|
<field name="default_code">SERV_ORDER</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="service_delivery" model="product.product">
|
||||||
|
<field name="name">Cost-plus Contract</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">200.0</field>
|
||||||
|
<field name="list_price">180.0</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">SERV_DEL</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="service_cost_01" model="product.product">
|
||||||
|
<field name="name">External Audit</field>
|
||||||
|
<field name="categ_id" ref="product_category_3"/>
|
||||||
|
<field name="standard_price">160</field>
|
||||||
|
<field name="list_price">180</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description">Example of products to invoice based on cost.</field>
|
||||||
|
<field name="default_code">SERV_COST</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_delivery_01" model="product.product">
|
||||||
|
<field name="name">Switch, 24 ports</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">55.0</field>
|
||||||
|
<field name="list_price">70.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">PROD_DEL</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_delivery_02" model="product.product">
|
||||||
|
<field name="name">Datacard</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">35.0</field>
|
||||||
|
<field name="list_price">40.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">PROD_DEL02</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_order_01" model="product.product">
|
||||||
|
<field name="name">Zed+ Antivirus</field>
|
||||||
|
<field name="categ_id" ref="product_category_4"/>
|
||||||
|
<field name="standard_price">235.0</field>
|
||||||
|
<field name="list_price">280.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">PROD_ORDER</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_1" model="product.product">
|
||||||
|
<field name="name">GAP Analysis Service</field>
|
||||||
|
<field name="categ_id" ref="product_category_3"/>
|
||||||
|
<field name="standard_price">20.5</field>
|
||||||
|
<field name="list_price">30.75</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="uom_id" ref="product_uom_hour"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_hour"/>
|
||||||
|
<field name="description">Example of products to invoice based on delivery.</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_2" model="product.product">
|
||||||
|
<field name="name">Support Services</field>
|
||||||
|
<field name="categ_id" ref="product_category_3"/>
|
||||||
|
<field name="standard_price">25.5</field>
|
||||||
|
<field name="list_price">38.25</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="uom_id" ref="product_uom_hour"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_hour"/>
|
||||||
|
<field name="description">Example of product to invoice based on delivery.</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="product_product_3" model="product.product">
|
||||||
|
<field name="name">Computer SC234</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="list_price">450.0</field>
|
||||||
|
<field name="standard_price">300.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description_sale">17" LCD Monitor
Processor AMD 8-Core</field>
|
||||||
|
<field name="default_code">PCSC234</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Ecommerce Variants and Products -->
|
||||||
|
|
||||||
|
<record id="product_attribute_1" model="product.attribute">
|
||||||
|
<field name="name">Memory</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_attribute_value_1" model="product.attribute.value">
|
||||||
|
<field name="name">16 GB</field>
|
||||||
|
<field name="attribute_id" ref="product_attribute_1"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_attribute_value_2" model="product.attribute.value">
|
||||||
|
<field name="name">32 GB</field>
|
||||||
|
<field name="attribute_id" ref="product_attribute_1"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_attribute_2" model="product.attribute">
|
||||||
|
<field name="name">Color</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_attribute_value_3" model="product.attribute.value">
|
||||||
|
<field name="name">White</field>
|
||||||
|
<field name="attribute_id" ref="product_attribute_2"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_attribute_value_4" model="product.attribute.value">
|
||||||
|
<field name="name">Black</field>
|
||||||
|
<field name="attribute_id" ref="product_attribute_2"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_attribute_3" model="product.attribute">
|
||||||
|
<field name="name">Wi-Fi</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_attribute_value_5" model="product.attribute.value">
|
||||||
|
<field name="name">2.4 GHz</field>
|
||||||
|
<field name="attribute_id" ref="product_attribute_3"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_4" model="product.product">
|
||||||
|
<field name="name">iPad Retina Display</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">500.0</field>
|
||||||
|
<field name="list_price">750.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description_sale">7.9‑inch (diagonal) LED-backlit, 128Gb
Dual-core A5 with quad-core graphics
FaceTime HD Camera, 1.2 MP Photos</field>
|
||||||
|
<field name="default_code">E-COM01</field>
|
||||||
|
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_1'), ref('product.product_attribute_value_3')])]"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_4b" model="product.product">
|
||||||
|
<field name="default_code">E-COM02</field>
|
||||||
|
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
|
||||||
|
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_1'), ref('product.product_attribute_value_4')])]"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_4c" model="product.product">
|
||||||
|
<field name="default_code">E-COM03</field>
|
||||||
|
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
|
||||||
|
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_2'), ref('product.product_attribute_value_3')])]"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_4d" model="product.product">
|
||||||
|
<field name="default_code">E-COM04</field>
|
||||||
|
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
|
||||||
|
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_2'), ref('product.product_attribute_value_4')])]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_attribute_line_1" model="product.attribute.line">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
|
||||||
|
<field name="attribute_id" ref="product_attribute_1"/>
|
||||||
|
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_1'), ref('product.product_attribute_value_2')])]"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_attribute_line_2" model="product.attribute.line">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
|
||||||
|
<field name="attribute_id" ref="product_attribute_2"/>
|
||||||
|
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_3'), ref('product.product_attribute_value_4')])]"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_attribute_line_3" model="product.attribute.line">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
|
||||||
|
<field name="attribute_id" ref="product_attribute_3"/>
|
||||||
|
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_5')])]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_4_product_template" model="product.template">
|
||||||
|
<field name="attribute_line_ids" eval="[(6,0,[ref('product.product_attribute_line_1'), ref('product.product_attribute_line_2'), ref('product.product_attribute_line_3')])]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_4d" model="product.product">
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_attribute_price_1" model="product.attribute.price">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_4_product_template"/>
|
||||||
|
<field name="value_id" ref="product_attribute_value_2"/>
|
||||||
|
<field name="price_extra">50.40</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_5b" model="product.product">
|
||||||
|
<field name="name">Bose Mini Bluetooth Speaker</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">140.0</field>
|
||||||
|
<field name="list_price">247.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description_sale">Bose's smallest portable Bluetooth speaker</field>
|
||||||
|
<field name="default_code">E-COM05</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record id="product_product_5" model="product.product">
|
||||||
|
<field name="name">Custom Computer (kit)</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">600.0</field>
|
||||||
|
<field name="list_price">147.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description">Custom computer shipped in kit.</field>
|
||||||
|
<field name="default_code">E-COM06</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_5c" model="product.product">
|
||||||
|
<field name="name">Parts Replacement</field>
|
||||||
|
<field name="categ_id" ref="product_category_3"/>
|
||||||
|
<field name="standard_price">600.0</field>
|
||||||
|
<field name="list_price">147.0</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_6" model="product.product">
|
||||||
|
<field name="name">iPad Mini</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">800.0</field>
|
||||||
|
<field name="list_price">320.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">E-COM07</field>
|
||||||
|
<field name='weight'>0.330</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_6c" model="product.product">
|
||||||
|
<field name="name">Repair</field>
|
||||||
|
<field name="categ_id" ref="product_category_3"/>
|
||||||
|
<field name="standard_price">800.0</field>
|
||||||
|
<field name="list_price">320.0</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_7" model="product.product">
|
||||||
|
<field name="name">Apple In-Ear Headphones</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">70.0</field>
|
||||||
|
<field name="list_price">79.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">E-COM08</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_8" model="product.product">
|
||||||
|
<field name="name">iMac</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">1299.0</field>
|
||||||
|
<field name="list_price">1799.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">E-COM09</field>
|
||||||
|
<field name='weight'>9.54</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_9" model="product.product">
|
||||||
|
<field name="name">Apple Wireless Keyboard</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">10.0</field>
|
||||||
|
<field name="list_price">47.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">E-COM10</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_10" model="product.product">
|
||||||
|
<field name="name">Mouse, Optical</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">12.50</field>
|
||||||
|
<field name="list_price">14</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">E-COM11</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_11" model="product.product">
|
||||||
|
<field name="name">iPod</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">14</field>
|
||||||
|
<field name="list_price">16.50</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">E-COM12</field>
|
||||||
|
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_1')])]"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_11b" model="product.product">
|
||||||
|
<field name="default_code">E-COM13</field>
|
||||||
|
<field name="product_tmpl_id" ref="product_product_11_product_template"/>
|
||||||
|
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_2')])]"/>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_11c" model="product.product">
|
||||||
|
<field name="name">Cleaning</field>
|
||||||
|
<field name="categ_id" ref="product_category_3"/>
|
||||||
|
<field name="standard_price">14</field>
|
||||||
|
<field name="list_price">16.50</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_attribute_line_4" model="product.attribute.line">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_11_product_template"/>
|
||||||
|
<field name="attribute_id" ref="product_attribute_1"/>
|
||||||
|
<field name="value_ids" eval="[(6,0,[ref('product.product_attribute_value_1'), ref('product.product_attribute_value_2')])]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_11_product_template" model="product.template">
|
||||||
|
<field name="attribute_line_ids" eval="[(6,0,[ref('product.product_attribute_line_4')])]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_attribute_price_2" model="product.attribute.price">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_11_product_template"/>
|
||||||
|
<field name="value_id" ref="product_attribute_value_2"/>
|
||||||
|
<field name="price_extra">6.40</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- MRP Demo Data-->
|
||||||
|
|
||||||
|
<record id="product_product_12" model="product.product">
|
||||||
|
<field name="name">Mouse, Wireless</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">18</field>
|
||||||
|
<field name="list_price">12.50</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">M-Wir</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_13" model="product.product">
|
||||||
|
<field name="name">RAM SR5</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">78.0</field>
|
||||||
|
<field name="list_price">85.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">RAM-SR5</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_16" model="product.product">
|
||||||
|
<field name="name">Computer Case</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">20.0</field>
|
||||||
|
<field name="list_price">25.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">C-Case</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_17" model="product.product">
|
||||||
|
<field name="name">HDD SH-1</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">860.0</field>
|
||||||
|
<field name="list_price">975.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">HDD-SH1</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_20" model="product.product">
|
||||||
|
<field name="name">Motherboard I9P57</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">1700.0</field>
|
||||||
|
<field name="list_price">1950.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">MBi9</field>
|
||||||
|
</record>
|
||||||
|
<record id="product_product_22" model="product.product">
|
||||||
|
<field name="name">Processor Core i5 2.70 Ghz</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">2010.0</field>
|
||||||
|
<field name="list_price">2100.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">CPUi5</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_24" model="product.product">
|
||||||
|
<field name="name">Graphics Card</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">876.0</field>
|
||||||
|
<field name="list_price">885.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="default_code">CARD</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_25" model="product.product">
|
||||||
|
<field name="name">Laptop E5023</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">2870.0</field>
|
||||||
|
<field name="list_price">2950.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description">17" Monitor, 4GB RAM
Standard-1294P Processor</field>
|
||||||
|
<field name="default_code">LAP-E5</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_27" model="product.product">
|
||||||
|
<field name="name">Laptop Customized</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">3300.0</field>
|
||||||
|
<field name="list_price">3645.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description">Custom Laptop based on customer's requirement.</field>
|
||||||
|
<field name="default_code">LAP-CUS</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="consu_delivery_03" model="product.product">
|
||||||
|
<field name="name">Basic Computer</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">25000.0</field>
|
||||||
|
<field name="list_price">23500.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description_sale">Dvorak keyboard
left-handed mouse</field>
|
||||||
|
<field name="default_code">CONS_DEL03</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="consu_delivery_02" model="product.product">
|
||||||
|
<field name="name">Little server</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">45000.0</field>
|
||||||
|
<field name="list_price">40000.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description_sale">raid 1, 512ECC ram</field>
|
||||||
|
<field name="default_code">CONS_DEL02</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="consu_delivery_01" model="product.product">
|
||||||
|
<field name="name">Server</field>
|
||||||
|
<field name="categ_id" ref="product_category_5"/>
|
||||||
|
<field name="standard_price">65000.0</field>
|
||||||
|
<field name="list_price">60000.0</field>
|
||||||
|
<field name="type">consu</field>
|
||||||
|
<field name="uom_id" ref="product_uom_unit"/>
|
||||||
|
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||||
|
<field name="description_sale">raid 10, 2048ECC ram</field>
|
||||||
|
<field name="default_code">CONS_DEL01</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Membership products -->
|
||||||
|
|
||||||
|
<record id="membership_0" model="product.product">
|
||||||
|
<field name="name">Gold Membership</field>
|
||||||
|
<field name="list_price">180</field>
|
||||||
|
<field name="categ_id" ref="product.product_category_1"/>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="membership_1" model="product.product">
|
||||||
|
<field name="name">Silver Membership</field>
|
||||||
|
<field name="categ_id" ref="product.product_category_1"/>
|
||||||
|
<field name="list_price">80</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="membership_2" model="product.product">
|
||||||
|
<field name="name">Basic Membership</field>
|
||||||
|
<field name="categ_id" ref="product.product_category_1"/>
|
||||||
|
<field name="list_price">40</field>
|
||||||
|
<field name="type">service</field>
|
||||||
|
<field name="active" eval="False"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Resource: product.supplierinfo
|
||||||
|
-->
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_1" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_6_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">750</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_2" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_6_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_4"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">790</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_2bis" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_6_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_4"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">3</field>
|
||||||
|
<field name="price">785</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_3" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_7_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">65</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_4" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_7_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_4"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">72</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_5" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_8_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">2</field>
|
||||||
|
<field name="min_qty">5</field>
|
||||||
|
<field name="price">1299</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_6" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_8_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_12"/>
|
||||||
|
<field name="delay">4</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">1399</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_7" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_10_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">2</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">12.50</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_8" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_11_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">2</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">14</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_9" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_13_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_4"/>
|
||||||
|
<field name="delay">5</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">78</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_10" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_16_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_3"/>
|
||||||
|
<field name="delay">1</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">20</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_11" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_17_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_2"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">860</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_12" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_20_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_4"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">1700</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_13" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_20_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">4</field>
|
||||||
|
<field name="min_qty">5</field>
|
||||||
|
<field name="price">1720</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_14" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_22_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_2"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">2010</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_15" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_24_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_2"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">876</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_16" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_25_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">8</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">2870</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_17" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_delivery_02_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_2"/>
|
||||||
|
<field name="delay">4</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">390</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_18" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_delivery_01_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_3"/>
|
||||||
|
<field name="delay">2</field>
|
||||||
|
<field name="min_qty">12</field>
|
||||||
|
<field name="price">90</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_19" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_delivery_01_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">4</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">66</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_20" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_delivery_02_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">5</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">35</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_21" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_delivery_01_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_12"/>
|
||||||
|
<field name="delay">7</field>
|
||||||
|
<field name="min_qty">1</field>
|
||||||
|
<field name="price">55</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_22" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_9_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_12"/>
|
||||||
|
<field name="delay">4</field>
|
||||||
|
<field name="min_qty">0</field>
|
||||||
|
<field name="price">10</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_23" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_27_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">10</field>
|
||||||
|
<field name="min_qty">0</field>
|
||||||
|
<field name="price">3300</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_24" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_12_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_1"/>
|
||||||
|
<field name="delay">3</field>
|
||||||
|
<field name="min_qty">0</field>
|
||||||
|
<field name="price">12.50</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_supplierinfo_25" model="product.supplierinfo">
|
||||||
|
<field name="product_tmpl_id" ref="product_product_12_product_template"/>
|
||||||
|
<field name="name" ref="base.res_partner_4"/>
|
||||||
|
<field name="delay">2</field>
|
||||||
|
<field name="min_qty">0</field>
|
||||||
|
<field name="price">13.50</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record forcecreate="True" id="property_product_pricelist_demo" model="ir.property">
|
||||||
|
<field name="name">property_product_pricelist</field>
|
||||||
|
<field name="fields_id" search="[('model','=','res.partner'),('name','=','property_product_pricelist')]"/>
|
||||||
|
<field name="value" eval="'product.pricelist,'+str(ref('list0'))"/>
|
||||||
|
<field name="res_id" eval="'res.partner,'+str(ref('base.partner_demo'))"/>
|
||||||
|
<field name="company_id" ref="base.main_company"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
110
app_base_chinese/demo/product_image_demo.xml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="false">
|
||||||
|
|
||||||
|
<record id="product_product_1" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_1-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_2" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_2-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_3" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_3-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_4" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_4-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_5" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_5-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_5b" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_5-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_6" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_6-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_7" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_7-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_8" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_8-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_9" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_9-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_10" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_10-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_11" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_11-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_12" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_12-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_13" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_13-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_16" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_16-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_17" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_17-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_20" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_20-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_22" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_22-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_24" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_24-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_25" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_25-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_27" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_27-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_order_01" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_43-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_delivery_02" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/product_product_46-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="membership_0" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/membership_0-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="membership_1" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/membership_1-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="membership_2" model="product.product">
|
||||||
|
<field name="image" type="base64" file="product/static/img/membership_2-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
21
app_base_chinese/demo/res_bank_demo.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<record model="res.bank" id="bank_ing">
|
||||||
|
<field name="name">ING</field>
|
||||||
|
<field name="bic">BBRUBEBB</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="res.bank" id="bank_crelan">
|
||||||
|
<field name="name">Crelan</field>
|
||||||
|
<field name="bic">NICABEBB</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="res.bank" id="bank_cbc">
|
||||||
|
<field name="name">CBC</field>
|
||||||
|
<field name="bic">CREGBEBB</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
251
app_base_chinese/demo/res_partner_demo.xml
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!--
|
||||||
|
Resource: res.partner.category
|
||||||
|
-->
|
||||||
|
|
||||||
|
<record id="res_partner_category_0" model="res.partner.category">
|
||||||
|
<field name="name">Partner</field>
|
||||||
|
<field name="color" eval="1"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_1" model="res.partner.category">
|
||||||
|
<field name="name">Vendor</field>
|
||||||
|
<field name="color" eval="2"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_2" model="res.partner.category">
|
||||||
|
<field name="name">Prospect</field>
|
||||||
|
<field name="color" eval="3"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_3" model="res.partner.category">
|
||||||
|
<field name="name">Employee</field>
|
||||||
|
<field name="color" eval="4"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_4" model="res.partner.category">
|
||||||
|
<field name="name">Gold</field>
|
||||||
|
<field name="color" eval="2"/>
|
||||||
|
<field name="parent_id" ref="res_partner_category_0"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_5" model="res.partner.category">
|
||||||
|
<field name="name">Silver</field>
|
||||||
|
<field name="color" eval="3"/>
|
||||||
|
<field name="parent_id" ref="res_partner_category_0"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_6" model="res.partner.category">
|
||||||
|
<field name="name">Bronze</field>
|
||||||
|
<field name="color" eval="4"/>
|
||||||
|
<field name="parent_id" ref="res_partner_category_0"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_7" model="res.partner.category">
|
||||||
|
<field name="name">IT Services</field>
|
||||||
|
<field name="color" eval="5"/>
|
||||||
|
<field name="parent_id" ref="res_partner_category_0"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_8" model="res.partner.category">
|
||||||
|
<field name="name">Consultancy Services</field>
|
||||||
|
<field name="color" eval="5"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_9" model="res.partner.category">
|
||||||
|
<field name="name">Components Buyer</field>
|
||||||
|
<field name="color" eval="6"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_11" model="res.partner.category">
|
||||||
|
<field name="name">Services</field>
|
||||||
|
<field name="color" eval="7"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_12" model="res.partner.category">
|
||||||
|
<field name="name">Office Supplies</field>
|
||||||
|
<field name="color" eval="8"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_13" model="res.partner.category">
|
||||||
|
<field name="name">Distributor</field>
|
||||||
|
<field name="color" eval="9"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_14" model="res.partner.category">
|
||||||
|
<field name="name">Manufacturer</field>
|
||||||
|
<field name="color" eval="10"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_15" model="res.partner.category">
|
||||||
|
<field name="name">Wholesaler</field>
|
||||||
|
<field name="color" eval="1"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_16" model="res.partner.category">
|
||||||
|
<field name="name">Retailer</field>
|
||||||
|
<field name="color" eval="2"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_17" model="res.partner.category">
|
||||||
|
<field name="name">Company Contact</field>
|
||||||
|
<field name="color" eval="3"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Resource: res.partner
|
||||||
|
-->
|
||||||
|
|
||||||
|
<record id="res_partner_1" model="res.partner">
|
||||||
|
<field name="name">ASUSTeK</field>
|
||||||
|
<field eval="[(6, 0, [ref('res_partner_category_13'), ref('res_partner_category_12')])]" name="category_id"/>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
<field name="is_company">1</field>
|
||||||
|
<field name="city">Taipei</field>
|
||||||
|
<field name="zip">106</field>
|
||||||
|
<field name="country_id" ref="base.tw"/>
|
||||||
|
<field name="street">31 Hong Kong street</field>
|
||||||
|
<field name="email">asusteK@yourcompany.example.com</field>
|
||||||
|
<field name="phone">(+886) (02) 4162 2023</field>
|
||||||
|
<field name="website">http://www.asustek.com</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_1-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_2" model="res.partner">
|
||||||
|
<field name="name">Agrolait</field>
|
||||||
|
<field eval="[(6, 0, [ref('base.res_partner_category_7'), ref('base.res_partner_category_9')])]" name="category_id"/>
|
||||||
|
<field name="is_company">1</field>
|
||||||
|
<field name="city">Wavre</field>
|
||||||
|
<field name="zip">1300</field>
|
||||||
|
<field name="country_id" ref="base.be"/>
|
||||||
|
<field name="street">69 rue de Namur</field>
|
||||||
|
<field name="email">agrolait@yourcompany.example.com</field>
|
||||||
|
<field name="phone">+32 10 588 558</field>
|
||||||
|
<field name="website">http://www.agrolait.com</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_2-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_3" model="res.partner">
|
||||||
|
<field name="name">China Export</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="[(6, 0, [ref('res_partner_category_8'), ref('res_partner_category_14')])]" name="category_id"/>
|
||||||
|
<field name="is_company">1</field>
|
||||||
|
<field name="city">Shanghai</field>
|
||||||
|
<field name="zip">200000</field>
|
||||||
|
<field model="res.country" name="country_id" search="[('name','=','China')]"/>
|
||||||
|
<field name="country_id" ref="base.cn"/>
|
||||||
|
<field name="street">52 Chop Suey street</field>
|
||||||
|
<field name="email">chinaexport@yourcompany.example.com</field>
|
||||||
|
<field name="phone">+86 21 6484 5671</field>
|
||||||
|
<field name="website">http://www.chinaexport.com/</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_3-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_4" model="res.partner">
|
||||||
|
<field name="name">Delta PC</field>
|
||||||
|
<field eval="[(6, 0, [ref('res_partner_category_13'), ref('res_partner_category_12')])]" name="category_id"/>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
<field eval="1" name="supplier"/>
|
||||||
|
<field name="is_company">1</field>
|
||||||
|
<field name="city">Fremont</field>
|
||||||
|
<field name="zip">94538</field>
|
||||||
|
<field name="country_id" ref="base.us"/>
|
||||||
|
<field model="res.country.state" name="state_id" search="[('code','ilike','ca')]"/>
|
||||||
|
<field name="street">3661 Station Street</field>
|
||||||
|
<field name="email">deltapc@yourcompany.example.com</field>
|
||||||
|
<field name="phone">+1 510 340 2385</field>
|
||||||
|
<field name="website">http://www.distribpc.com/</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_4-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_10" model="res.partner">
|
||||||
|
<field name="name">The Jackson Group</field>
|
||||||
|
<field eval="[(6, 0, [ref('res_partner_category_4'), ref('res_partner_category_7')])]" name="category_id"/>
|
||||||
|
<field name="is_company">1</field>
|
||||||
|
<field name="city">Miami</field>
|
||||||
|
<field name="zip">33169</field>
|
||||||
|
<field name="country_id" ref="base.us"/>
|
||||||
|
<field model="res.country.state" name="state_id" search="[('code','=','FL')]"/>
|
||||||
|
<field name="email">jackson@yourcompany.example.com</field>
|
||||||
|
<field name="phone">+1 786 525 0724</field>
|
||||||
|
<field name="street">3203 Lamberts Branch Road</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_10-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_12" model="res.partner">
|
||||||
|
<field name="name">Camptocamp</field>
|
||||||
|
<field eval="[(6, 0, [ref('res_partner_category_11'), ref('res_partner_category_4')])]" name="category_id"/>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field name="customer">1</field>
|
||||||
|
<field name="is_company">1</field>
|
||||||
|
<field name="city">Le Bourget du Lac</field>
|
||||||
|
<field name="zip">73377</field>
|
||||||
|
<field name="phone">+33 4 49 23 44 54</field>
|
||||||
|
<field name="country_id" ref="base.fr"/>
|
||||||
|
<field name="street">93, Press Avenue</field>
|
||||||
|
<field name="email">camptocamp@yourcompany.example.com</field>
|
||||||
|
<field name="website">http://www.camptocamp.com</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_12-image.jpg"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_18" model="res.partner">
|
||||||
|
<field name="name">Think Big Systems</field>
|
||||||
|
<field name="is_company">1</field>
|
||||||
|
<field eval="[(6, 0, [ref('res_partner_category_5')])]" name="category_id"/>
|
||||||
|
<field name="city">London</field>
|
||||||
|
<field name="email">thinkbig@yourcompany.example.com</field>
|
||||||
|
<field name="phone">+1 857 349 3049</field>
|
||||||
|
<field name="country_id" ref="base.uk"/>
|
||||||
|
<field name="street">89 Lingfield Tower</field>
|
||||||
|
<field name="website">http://www.think-big.com</field>
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_18-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="res_partner_address_1" model="res.partner">
|
||||||
|
<field name="name">Tang Tsui</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_1')"/>
|
||||||
|
<field name="function">Service Manager</field>
|
||||||
|
<field name="email">tang@asustek.com</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_address_2" model="res.partner">
|
||||||
|
<field name="name">Joseph Walters</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_1')"/>
|
||||||
|
<field name="function">Store Manager</field>
|
||||||
|
<field name="email">joseph.walters@asustek.com</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_address_7" model="res.partner">
|
||||||
|
<field name="name">Richard Ellis</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_4')"/>
|
||||||
|
<field name="function">Production Supervisor</field>
|
||||||
|
<field name="email">richard.ellis@deltapc.example.com</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_address_12" model="res.partner">
|
||||||
|
<field name="name">James Miller</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_1')"/>
|
||||||
|
<field name="function">Electrical Supervisor</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_address_31" model="res.partner">
|
||||||
|
<field name="name">Edward Foster</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_2')"/>
|
||||||
|
<field name="function">Sales Representative</field>
|
||||||
|
<field name="email">efoster@seagate.com</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_address_27" model="res.partner">
|
||||||
|
<field name="name">Arthur Gomez</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_1')"/>
|
||||||
|
<field name="function">Software Developer</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_address_26" model="res.partner">
|
||||||
|
<field name="name">Julia Rivero</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_1')"/>
|
||||||
|
<field name="function">Technical Director</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_address_35" model="res.partner">
|
||||||
|
<field name="name">Peter Mitchell</field>
|
||||||
|
<field name="parent_id" eval="ref('res_partner_1')"/>
|
||||||
|
<field name="function">Store Manager</field>
|
||||||
|
<field name="supplier">1</field>
|
||||||
|
<field eval="0" name="customer"/>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
|
|
||||||
98
app_base_chinese/demo/res_partner_image_demo.xml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="01>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_1" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_1-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_2" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_2-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_4" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_4-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_5" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_5-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_7" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_7-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_10" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_10-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_11" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_11-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_13" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_13-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_14" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_14-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_15" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_15-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_16" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_16-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_17" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_17-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_18" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_18-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_24" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_24-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_25" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_25-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_27" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_27-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_28" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_28-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_30" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_30-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_32" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_32-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_33" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_33-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_address_34" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_address_34-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_main1" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_main1-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="base.res_partner_main2" model="res.partner">
|
||||||
|
<field name="image" type="base64" file="base/static/img/res_partner_main2-image.png"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
55
app_base_chinese/hooks.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 2017-11-22
|
||||||
|
# author: 广州尚鹏,https://www.sunpop.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of Sunpop
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
# Odoo在线中文用户手册(长期更新)
|
||||||
|
# https://www.sunpop.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.sunpop.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.sunpop.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID, _
|
||||||
|
|
||||||
|
|
||||||
|
def pre_init_hook(cr):
|
||||||
|
"""
|
||||||
|
数据初始化,只在安装时执行,更新时不执行
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def post_init_hook(cr, registry):
|
||||||
|
"""
|
||||||
|
数据初始化,只在安装后执行,更新时不执行
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
ids = env['product.category'].sudo().with_context(lang='zh_CN').search([
|
||||||
|
('parent_id', '!=', False)
|
||||||
|
], order='parent_path')
|
||||||
|
for rec in ids:
|
||||||
|
rec._compute_complete_name()
|
||||||
|
ids = env['stock.location'].sudo().with_context(lang='zh_CN').search([
|
||||||
|
('location_id', '!=', False),
|
||||||
|
('usage', '!=', 'views'),
|
||||||
|
], order='parent_path')
|
||||||
|
for rec in ids:
|
||||||
|
rec._compute_complete_name()
|
||||||
|
# 超级用户改时区为中国
|
||||||
|
ids = env['res.users'].sudo().with_context(lang='zh_CN').browse([1, 2])
|
||||||
|
ids.write({'tz': "Etc/GMT-8"})
|
||||||
|
except Exception as e:
|
||||||
|
raise Warning(e)
|
||||||
|
|
||||||
|
def uninstall_hook(cr, registry):
|
||||||
|
"""
|
||||||
|
数据初始化,卸载时执行
|
||||||
|
"""
|
||||||
|
pass
|
||||||
130
app_base_chinese/i18n/zh_CN.po
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * app_base_chinese
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 13.0+e\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-04-10 18:20+0000\n"
|
||||||
|
"PO-Revision-Date: 2020-04-10 18:20+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_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_account_tax_group__active
|
||||||
|
msgid "Active"
|
||||||
|
msgstr "激活"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model,name:app_base_chinese.model_base
|
||||||
|
msgid "Base"
|
||||||
|
msgstr "基础"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_product_category__complete_name
|
||||||
|
msgid "Complete Name"
|
||||||
|
msgstr "完整名称"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model,name:app_base_chinese.model_res_partner
|
||||||
|
msgid "Contact"
|
||||||
|
msgstr "联系人"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_base_chinese.app_res_partner_kanban_view
|
||||||
|
msgid "Credit:"
|
||||||
|
msgstr "信用额:"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model,name:app_base_chinese.model_res_currency
|
||||||
|
msgid "Currency"
|
||||||
|
msgstr "币种"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,help:app_base_chinese.field_res_currency__sequence
|
||||||
|
msgid "Determine the display order. Sort ascending."
|
||||||
|
msgstr "决定显示顺序,数字越小排序越前"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_partner__name_en_US
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_users__name_en_US
|
||||||
|
msgid "English Name"
|
||||||
|
msgstr "英文名"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_base_chinese.app_view_sale_advance_payment_inv
|
||||||
|
msgid "FaPiao / Invoice Orders"
|
||||||
|
msgstr "创建收据/发票"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_partner__fax
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_users__fax
|
||||||
|
msgid "Fax"
|
||||||
|
msgstr "传真"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_stock_location__complete_name
|
||||||
|
msgid "Full Location Name"
|
||||||
|
msgstr "完整的位置名称"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model,name:app_base_chinese.model_stock_location
|
||||||
|
msgid "Inventory Locations"
|
||||||
|
msgstr "库存位置"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_base_chinese.app_view_order_form
|
||||||
|
msgid "Invoices/FaPiao"
|
||||||
|
msgstr "收据/发票"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_base_chinese.app_view_order_form
|
||||||
|
msgid "Make Collections"
|
||||||
|
msgstr "收款"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_partner__name
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_users__name
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "名称"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model_terms:ir.ui.view,arch_db:app_base_chinese.app_view_move_form
|
||||||
|
msgid "Post Entry"
|
||||||
|
msgstr "过帐"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model,name:app_base_chinese.model_product_category
|
||||||
|
msgid "Product Category"
|
||||||
|
msgstr "品类"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_currency__sequence
|
||||||
|
msgid "Sequence"
|
||||||
|
msgstr "排序"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,help:app_base_chinese.field_account_tax_group__active
|
||||||
|
msgid "Set active to false to hide the tax without removing it."
|
||||||
|
msgstr "请不要删除指定税率,可以将其归档。"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_partner__short_name
|
||||||
|
#: model:ir.model.fields,field_description:app_base_chinese.field_res_users__short_name
|
||||||
|
msgid "Short Name"
|
||||||
|
msgstr "简称"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.model,name:app_base_chinese.model_account_tax_group
|
||||||
|
msgid "Tax Group"
|
||||||
|
msgstr "税组"
|
||||||
|
|
||||||
|
#. module: app_base_chinese
|
||||||
|
#: model:ir.sequence,name:app_base_chinese.sequence_product_category_normal
|
||||||
|
msgid "产品目录常规编号规则"
|
||||||
|
msgstr ""
|
||||||
1
app_base_chinese/ir/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
10
app_base_chinese/models/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import res_partner
|
||||||
|
from . import res_currency
|
||||||
|
from . import product_category
|
||||||
|
from . import stock_location
|
||||||
|
from . import account_tax_group
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
9
app_base_chinese/models/account_tax_group.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _, tools
|
||||||
|
|
||||||
|
class AccountTaxGroup(models.Model):
|
||||||
|
_inherit = 'account.tax.group'
|
||||||
|
|
||||||
|
active = fields.Boolean(default=True, help="Set active to false to hide the tax without removing it.")
|
||||||
|
|
||||||
14
app_base_chinese/models/product_category.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class ProductCategory(models.Model):
|
||||||
|
_inherit = "product.category"
|
||||||
|
|
||||||
|
complete_name = fields.Char(
|
||||||
|
'Complete Name', compute='_compute_complete_name',
|
||||||
|
store=True, translate=True)
|
||||||
17
app_base_chinese/models/res_company.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, models, fields, _
|
||||||
|
from odoo.exceptions import UserError, ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class ResCompany(models.Model):
|
||||||
|
_inherit = 'res.company'
|
||||||
|
|
||||||
|
short_name = fields.Char('Short Name', related='partner_id.short_name', readonly=False)
|
||||||
|
|
||||||
|
# 当传参 show_short_company 时,只显示简称
|
||||||
|
def name_get(self):
|
||||||
|
if self._context.get('show_short_company'):
|
||||||
|
return [(value.id, "%s" % (value.short_name if value.short_name else value.name)) for value in self]
|
||||||
|
else:
|
||||||
|
return super().name_get()
|
||||||
11
app_base_chinese/models/res_country.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class Country(models.Model):
|
||||||
|
_inherit = 'res.country'
|
||||||
|
_order = 'sequence,name'
|
||||||
|
|
||||||
|
sequence = fields.Integer('Sequence', help="Determine the display order", default=99)
|
||||||
53
app_base_chinese/models/res_currency.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class ResCurrency(models.Model):
|
||||||
|
_inherit = 'res.currency'
|
||||||
|
_order = 'active desc, sequence, name'
|
||||||
|
|
||||||
|
sequence = fields.Integer('Sequence', default=10, help="Determine the display order. Sort ascending.")
|
||||||
|
|
||||||
|
def rmb_upper(self, value):
|
||||||
|
"""
|
||||||
|
人民币大写
|
||||||
|
传入浮点类型的值返回 unicode 字符串
|
||||||
|
:param 传入阿拉伯数字
|
||||||
|
:return 返回值是对应阿拉伯数字的绝对值的中文数字
|
||||||
|
"""
|
||||||
|
if self.name != 'CNY':
|
||||||
|
return
|
||||||
|
rmbmap = [u"零", u"壹", u"贰", u"叁", u"肆", u"伍", u"陆", u"柒", u"捌", u"玖"]
|
||||||
|
unit = [u"分", u"角", u"元", u"拾", u"佰", u"仟", u"万", u"拾", u"佰", u"仟", u"亿",
|
||||||
|
u"拾", u"佰", u"仟", u"万", u"拾", u"佰", u"仟", u"兆"]
|
||||||
|
# 冲红负数处理
|
||||||
|
xflag = 0
|
||||||
|
if value < 0:
|
||||||
|
xflag = value
|
||||||
|
value = abs(value)
|
||||||
|
# 先把value 数字进行格式化保留两位小数,转成字符串然后去除小数点
|
||||||
|
nums = list(map(int, list(str('%0.2f' % value).replace('.', ''))))
|
||||||
|
words = []
|
||||||
|
zflag = 0 # 标记连续0次数,以删除万字,或适时插入零字
|
||||||
|
start = len(nums) - 3
|
||||||
|
for i in range(start, -3, -1): # 使i对应实际位数,负数为角分
|
||||||
|
# 大部分情况对应数字不等于零 或者是刚开始循环
|
||||||
|
if 0 != nums[start - i] or len(words) == 0:
|
||||||
|
if zflag:
|
||||||
|
words.append(rmbmap[0])
|
||||||
|
zflag = 0
|
||||||
|
words.append(rmbmap[nums[start - i]]) # 数字对应的中文字符
|
||||||
|
words.append(unit[i + 2]) # 列表此位置的单位
|
||||||
|
# 控制‘万/元’ 万和元比较特殊,如2拾万和2拾1万 无论有没有这个1 万字是必须的
|
||||||
|
elif 0 == i or (0 == i % 4 and zflag < 3):
|
||||||
|
# 上面那种情况定义了 2拾1万 的显示 这个是特殊对待的 2拾万(一类)的显示
|
||||||
|
words.append(unit[i + 2])
|
||||||
|
# 元(控制条件为 0 == i )和万(控制条为(0 == i % 4 and zflag < 3))的情况的处理是一样的
|
||||||
|
zflag = 0
|
||||||
|
else:
|
||||||
|
zflag += 1
|
||||||
|
if words[-1] != unit[0]: # 结尾非‘分’补整字 最小单位 如果最后一个字符不是最小单位(分)则要加一个整字
|
||||||
|
words.append(u"整")
|
||||||
|
if xflag < 0: # 如果为负数则要在数字前面加上‘负’字
|
||||||
|
words.insert(0, u"负")
|
||||||
|
return ''.join(words)
|
||||||
33
app_base_chinese/models/res_partner.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from odoo import api, models, fields, _
|
||||||
|
from odoo.exceptions import UserError, ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
name = fields.Char(index=True, translate=True)
|
||||||
|
short_name = fields.Char('Short Name') # 简称
|
||||||
|
fax = fields.Char('Fax') # 简称
|
||||||
|
|
||||||
|
# 增加地址显示中的手机号与电话号码
|
||||||
|
# 选项 show_address 开启则增加显示手机与电话号
|
||||||
|
def _get_name(self):
|
||||||
|
name = super(ResPartner, self)._get_name() or ''
|
||||||
|
partner = self
|
||||||
|
if self._context.get('show_address'):
|
||||||
|
if partner.mobile and partner.phone:
|
||||||
|
name = name + "\n" + partner.mobile + "," + partner.phone
|
||||||
|
elif partner.mobile:
|
||||||
|
name = name + "\n" + partner.mobile
|
||||||
|
elif partner.phone:
|
||||||
|
name = name + "\n" + partner.phone
|
||||||
|
return name
|
||||||
|
|
||||||
|
@api.model_create_multi
|
||||||
|
def create(self, vals_list):
|
||||||
|
for values in vals_list:
|
||||||
|
if 'lang' not in values:
|
||||||
|
values['lang'] = 'zh_CN'
|
||||||
|
return super(ResPartner, self).create(vals_list)
|
||||||
12
app_base_chinese/models/stock_location.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import api, fields, models, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class Location(models.Model):
|
||||||
|
_inherit = "stock.location"
|
||||||
|
|
||||||
|
complete_name = fields.Char("Full Location Name", compute='_compute_complete_name', store=True, translate=True)
|
||||||
2
app_base_chinese/report/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
1
app_base_chinese/res/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
BIN
app_base_chinese/static/description/banner.jpg
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
app_base_chinese/static/description/cnreadme.jpg
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
app_base_chinese/static/description/demo1.jpg
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
app_base_chinese/static/description/demo2.jpg
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
app_base_chinese/static/description/demo3.jpg
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
app_base_chinese/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
96
app_base_chinese/static/description/index.html
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced" >
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan">App base chinese. Local customize for china user </h2>
|
||||||
|
<h3 class="oe_slogan">Set all chinese default value. Like Default country, timezone, currency, partner... </h3>
|
||||||
|
<div class="oe_row">
|
||||||
|
<h3>Lastest update: v13.20.04.11</h3>
|
||||||
|
<div class="oe_span12">
|
||||||
|
<img class="oe_demo oe_screenshot" src="banner.jpg">
|
||||||
|
</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>
|
||||||
|
Chinese feature enhance.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-check-square-o text-primary"></i>
|
||||||
|
Set Default country, state, timezone, currency.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-check-square-o text-primary"></i>
|
||||||
|
Set partner to chinese format.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-check-square-o text-primary"></i>
|
||||||
|
Fix Category Display not in english bug.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="oe_span12">
|
||||||
|
<img class="oe_demo oe_screenshot" src="cnreadme.jpg">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h4 class="oe_slogan">Set Default country, state, timezone, currency.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot">
|
||||||
|
<img src="demo1.jpg">
|
||||||
|
</div>
|
||||||
|
<div class="oe_demo oe_screenshot">
|
||||||
|
<img src="demo2.jpg">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h4 class="oe_slogan">Fix Category Display not in english bug.</h4>
|
||||||
|
<div class="oe_demo oe_screenshot">
|
||||||
|
<img src="demo3.jpg">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container oe_dark">
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<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.sunpop.cn</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<h1>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=Sunpop.cn">Supop.cn Odoo Addons</a>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
26
app_base_chinese/static/src/scss/app_style.scss
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
.o_form_view .o_address_format {
|
||||||
|
.o_address_country {
|
||||||
|
display: -webkit-inline-flex;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 48%;
|
||||||
|
margin-right: 2%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.o_form_view.o_form_editable .o_address_format {
|
||||||
|
div.o_address_state {
|
||||||
|
width: 50%;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.o_address_city{
|
||||||
|
width: 48%;
|
||||||
|
margin-right: 2%;
|
||||||
|
}
|
||||||
|
.o_address_city_id{
|
||||||
|
width: 48%;
|
||||||
|
margin-right: 2%;
|
||||||
|
}
|
||||||
|
input.o_address_zip{
|
||||||
|
width: 50%;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app_base_chinese/static/src/xml/views.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<templates id="template" xml:space="preserve">
|
||||||
|
<!--改search中英文-->
|
||||||
|
<t t-extend="SearchPanel.Category">
|
||||||
|
<t t-jquery="div.alert" t-operation="replace">
|
||||||
|
<div t-if="category.limitAttained" class="alert alert-warning">
|
||||||
|
<span>性能原因,只支持 200 条记录的显示</span>
|
||||||
|
<span t-if="category.parentField">记录没有树状结构</span>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
19
app_base_chinese/views/account_move_views.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- VIEWS -->
|
||||||
|
|
||||||
|
<record id="app_view_move_form" model="ir.ui.view">
|
||||||
|
<field name="name">app.account.move.form</field>
|
||||||
|
<field name="model">account.move</field>
|
||||||
|
<field name="inherit_id" ref="account.view_move_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- 过帐操作-->
|
||||||
|
<xpath expr="//header//button[@name='action_post']" position="attributes">
|
||||||
|
<attribute name="string">Post Entry</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
27
app_base_chinese/views/ir_default_views.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- currency视图, tree -->
|
||||||
|
<record id="app_view_currency_tree" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.currency.tree</field>
|
||||||
|
<field name="model">res.currency</field>
|
||||||
|
<field name="inherit_id" ref="base.view_currency_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='name']" position="before">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!-- currency视图, form -->
|
||||||
|
<record id="app_view_currency_form" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.currency.form</field>
|
||||||
|
<field name="model">res.currency</field>
|
||||||
|
<field name="inherit_id" ref="base.view_currency_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='position']" position="after">
|
||||||
|
<field name="sequence"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
26
app_base_chinese/views/res_company_views.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<record id="app_view_company_tree" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.company.form</field>
|
||||||
|
<field name="model">res.company</field>
|
||||||
|
<field name="inherit_id" ref="base.view_company_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='name']" position="after">
|
||||||
|
<field name="short_name"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="app_view_company_form" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.company.form</field>
|
||||||
|
<field name="model">res.company</field>
|
||||||
|
<field name="inherit_id" ref="base.view_company_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='phone']" position="before">
|
||||||
|
<field name='short_name'/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
27
app_base_chinese/views/res_currency_views.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- currency视图, tree -->
|
||||||
|
<record id="app_ir_default_tree_view" model="ir.ui.view">
|
||||||
|
<field name="name">app.ir.default tree view</field>
|
||||||
|
<field name="model">ir.default</field>
|
||||||
|
<field name="inherit_id" ref="base.ir_default_tree_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='json_value']" position="after">
|
||||||
|
<field name="condition"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!-- currency视图, form -->
|
||||||
|
<record id="app_ir_default_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">app.ir.default form view</field>
|
||||||
|
<field name="model">ir.default</field>
|
||||||
|
<field name="inherit_id" ref="base.ir_default_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='json_value']" position="after">
|
||||||
|
<field name="condition"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
171
app_base_chinese/views/res_partner_views.xml
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- Partner Form视图,用继承方式改写2018-11-21,ivan -->
|
||||||
|
<record id="app_view_partner_form" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.partner.form</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- 客户编号设计在重要位置,放到 app_partner_auto_reference -->
|
||||||
|
<!--<xpath expr="//field[@name='type']" position="after">-->
|
||||||
|
<!--<xpath expr="//field[@name='ref']" position="move"/>-->
|
||||||
|
<!--</xpath>-->
|
||||||
|
<xpath expr="//field[@name='type']" position="after">
|
||||||
|
<field name="short_name"/>
|
||||||
|
</xpath>
|
||||||
|
<!-- 信用额度 -->
|
||||||
|
<xpath expr="//field[@name='category_id']" position="after">
|
||||||
|
<field name="credit_limit"/>
|
||||||
|
</xpath>
|
||||||
|
<!--res.partner客户 地址中国化 -->
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='country_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='state_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='city']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='zip']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<!-- res.partner联系人 form -->
|
||||||
|
<!--联系人,如果是个人类型,不允许其下再有联系人-->
|
||||||
|
<!--调整,电商情况,一个人有多个地址-->
|
||||||
|
<!-- <xpath expr="//notebook/page[1]" position="attributes">-->
|
||||||
|
<!-- <attribute name="attrs">{'invisible': [('is_company','!=', True)]}</attribute>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- 所有类型联系人都要填姓名 -->
|
||||||
|
<xpath expr="//page//field[@name='child_ids']//form[1]//field[@name='name']" position="attributes">
|
||||||
|
<attribute name="required">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 联系人也显示地址,中国格式 -->
|
||||||
|
<!--res.partner客户 地址中国化 -->
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='country_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='state_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='city']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='zip']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='child_ids']//form[1]//field[@name='email']" position="before">
|
||||||
|
<field name="category_id" widget="many2many_tags"/>
|
||||||
|
</xpath>
|
||||||
|
<!-- res.partner联系人 kanban -->
|
||||||
|
<!-- 显示类型和地址等 -->
|
||||||
|
<xpath expr="//field[@name='child_ids']//kanban[1]//templates//field[@name='name']" position="before">
|
||||||
|
<t t-if="record.ref and record.ref.raw_value">[<field name="ref"/>]</t>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='child_ids']//kanban[1]//templates//field[@name='name']" position="after">
|
||||||
|
<div t-if="record.type and record.type.raw_value"><field name="type"/></div>
|
||||||
|
<div t-if="record.category_id and record.category_id.raw_value"><field name="category_id" widget="many2many_tags"/></div>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--res.partner open地址中国化 -->
|
||||||
|
<record id="app_view_partner_address_form" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.partner.form.address</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_address_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='country_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='state_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='city']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='zip']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='website']" position="after">
|
||||||
|
<field name="category_id" widget="many2many_tags"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--res.partner open地址中国化 -->
|
||||||
|
<!-- <record id="app_view_partner_short_form" model="ir.ui.view">-->
|
||||||
|
<!-- <field name="name">app.res.partner.form</field>-->
|
||||||
|
<!-- <field name="model">res.partner</field>-->
|
||||||
|
<!-- <field name="inherit_id" ref="base.view_partner_short_form"/>-->
|
||||||
|
<!-- <field name="arch" type="xml">-->
|
||||||
|
<!-- <xpath expr="//field[@name='street']" position="before">-->
|
||||||
|
<!-- <xpath expr="//field[@name='country_id']" position="move"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- <xpath expr="//field[@name='street']" position="before">-->
|
||||||
|
<!-- <xpath expr="//field[@name='state_id']" position="move"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- <xpath expr="//field[@name='street']" position="before">-->
|
||||||
|
<!-- <xpath expr="//field[@name='city']" position="move"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- <xpath expr="//field[@name='street']" position="before">-->
|
||||||
|
<!-- <xpath expr="//field[@name='zip']" position="move"/>-->
|
||||||
|
<!-- </xpath>-->
|
||||||
|
<!-- </field>-->
|
||||||
|
<!-- </record>-->
|
||||||
|
<!--res.partner 私人地址中国化 -->
|
||||||
|
<record id="app_res_partner_view_form_private" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.partner.view.form.private</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.res_partner_view_form_private"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='country_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='state_id']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='city']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='street']" position="before">
|
||||||
|
<xpath expr="//field[@name='zip']" position="move"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="app_view_partner_tree" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.partner.tree</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='email']" position="after">
|
||||||
|
<field name="category_id" widget="many2many_tags" optional="show"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Partner kanban 视图,用继承方式改写2017-11-23,ivan-->
|
||||||
|
<record id="app_res_partner_kanban_view" model="ir.ui.view">
|
||||||
|
<field name="name">app.res.partner.kanban</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.res_partner_kanban_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//templates" position="before">
|
||||||
|
<field name="credit_limit"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//div[hasclass('oe_kanban_details')]/ul" position="inside">
|
||||||
|
<li t-if="record.credit_limit and record.credit_limit.raw_value">
|
||||||
|
Credit:<field name="credit_limit"/>
|
||||||
|
</li>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
<!--end客户-->
|
||||||
|
|
||||||
|
<!-- 为供应商菜单单独增加筛选器 2017-11-13 -->
|
||||||
|
<!--end供应商-->
|
||||||
|
|
||||||
|
<!-- 所有合作伙伴 2017-11-13 -->
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
24
app_base_chinese/views/sale_order_views.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!-- 改收款相关操作显示 -->
|
||||||
|
<record id="app_view_order_form" model="ir.ui.view">
|
||||||
|
<field name="name">app.sale.order.form</field>
|
||||||
|
<field name="model">sale.order</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_order_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- 全款操作-->
|
||||||
|
<xpath expr="//header//button[@name='%(sale.action_view_sale_advance_payment_inv)d'][1]" position="attributes">
|
||||||
|
<attribute name="string">Make Collections</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 尾款操作-->
|
||||||
|
<xpath expr="//header//button[@name='%(sale.action_view_sale_advance_payment_inv)d'][1]" position="attributes">
|
||||||
|
<attribute name="string">Make Collections</attribute>
|
||||||
|
</xpath>
|
||||||
|
<!-- 查看收据列表-->
|
||||||
|
<xpath expr="//div[hasclass('oe_button_box')]//field[@name='invoice_count']" position="attributes">
|
||||||
|
<attribute name="string">Invoices/FaPiao</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
8
app_base_chinese/views/templates.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<template id="_assets_backend" name="app_base_chinese assets backend" inherit_id="web.assets_backend">
|
||||||
|
<xpath expr="//link[last()]" position="after">
|
||||||
|
<link rel="stylesheet" type="text/scss" href="/app_base_chinese/static/src/scss/app_style.scss"/>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
|
</odoo>
|
||||||
21
app_base_chinese/wizard/sale_make_invoice_advance_views.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="sale.view_sale_advance_payment_inv" model="ir.ui.view">
|
||||||
|
<field name="name">FaPiao / Invoice Orders</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="app_view_sale_advance_payment_inv" model="ir.ui.view">
|
||||||
|
<field name="name">app.account.move.form</field>
|
||||||
|
<field name="model">sale.advance.payment.inv</field>
|
||||||
|
<field name="inherit_id" ref="sale.view_sale_advance_payment_inv"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//form" position="attributes">
|
||||||
|
<attribute name="string">FaPiao / Invoice Orders</attribute>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="sale.action_view_sale_advance_payment_inv" model="ir.actions.act_window">
|
||||||
|
<field name="name">Create FaPiao / Invoice</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
7
app_common/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import controllers
|
||||||
|
from . import models
|
||||||
|
from .hooks import pre_init_hook
|
||||||
|
from .hooks import post_init_hook
|
||||||
|
from .hooks import uninstall_hook
|
||||||
84
app_common/__manifest__.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 20120-01-05
|
||||||
|
# author: 广州尚鹏,https://www.sunpop.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of Sunpop
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||||
|
|
||||||
|
# Odoo12在线用户手册(长期更新)
|
||||||
|
# https://www.sunpop.cn/documentation/user/12.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo12在线开发者手册(长期更新)
|
||||||
|
# https://www.sunpop.cn/documentation/12.0/index.html
|
||||||
|
|
||||||
|
# Odoo10在线中文用户手册(长期更新)
|
||||||
|
# https://www.sunpop.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.sunpop.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.sunpop.cn/odoo10_developer_document_offline/
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (C) 2009-TODAY Sunpop.cn Ltd. https://www.sunpop.cn
|
||||||
|
# Author: Ivan Deng,300883@qq.com
|
||||||
|
# You can modify it under the terms of the GNU LESSER
|
||||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
||||||
|
# See <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies
|
||||||
|
# of the Software or modified copies of the Software.
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': "Sunpop Odooapp Common Func",
|
||||||
|
'version': '15.21.11.30',
|
||||||
|
'author': 'Sunpop.cn',
|
||||||
|
'category': 'Base',
|
||||||
|
'website': 'https://www.sunpop.cn',
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
'sequence': 2,
|
||||||
|
'price': 0.00,
|
||||||
|
'currency': 'EUR',
|
||||||
|
'images': ['static/description/banner.png'],
|
||||||
|
'summary': '''
|
||||||
|
Core for common use sunpop apps.
|
||||||
|
基础核心,必须没有要被依赖字段及视图等,实现auto_install
|
||||||
|
''',
|
||||||
|
'description': '''
|
||||||
|
Support Odoo 15, Enterprise and Community Edition
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3. Multi-language Support.
|
||||||
|
4. Multi-Company Support.
|
||||||
|
5. Support Odoo 15, Enterprise and Community Edition
|
||||||
|
==========
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3. 多语言支持
|
||||||
|
4. 多公司支持
|
||||||
|
5. Odoo 15, 企业版,社区版,多版本支持
|
||||||
|
''',
|
||||||
|
'depends': [
|
||||||
|
'base',
|
||||||
|
'web',
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
# 'security/*.xml',
|
||||||
|
# 'security/ir.model.access.csv',
|
||||||
|
# 'data/.xml',
|
||||||
|
'views/ir_cron_views.xml',
|
||||||
|
# 'report/.xml',
|
||||||
|
],
|
||||||
|
'qweb': [
|
||||||
|
'static/src/xml/*.xml',
|
||||||
|
],
|
||||||
|
'demo': [],
|
||||||
|
# 'pre_init_hook': 'pre_init_hook',
|
||||||
|
# 'post_init_hook': 'post_init_hook',
|
||||||
|
# 'uninstall_hook': 'uninstall_hook',
|
||||||
|
'installable': True,
|
||||||
|
'application': True,
|
||||||
|
'auto_install': True,
|
||||||
|
}
|
||||||
3
app_common/controllers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import main
|
||||||
95
app_common/controllers/main.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# -*- 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 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('/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 = self.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):
|
||||||
|
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
|
||||||
|
# 微信浏览器,开发工具,小程序,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
|
||||||
|
|
||||||
|
utype = 'web'
|
||||||
|
# todo: 引入现成 py lib,处理企业微信
|
||||||
|
if 'MicroMessenger' in ua and 'webdebugger' not in ua and ('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
|
||||||
|
|
||||||
|
|
||||||
|
def haversine(lon1, lat1, lon2, lat2):
|
||||||
|
# 计算地图上两点的距离
|
||||||
|
# in:经度1,纬度1,经度2,纬度2 (十进制度数)
|
||||||
|
# out: 距离(米)
|
||||||
|
"""
|
||||||
|
Calculate the great circle distance between two points
|
||||||
|
on the earth (specified in decimal degrees)
|
||||||
|
"""
|
||||||
|
# 将十进制度数转化为弧度
|
||||||
|
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
|
||||||
|
|
||||||
|
# haversine公式
|
||||||
|
dlon = lon2 - lon1
|
||||||
|
dlat = lat2 - lat1
|
||||||
|
a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
|
||||||
|
c = 2 * asin(sqrt(a))
|
||||||
|
r = 6371 # 地球平均半径,单位为公里
|
||||||
|
return c * r * 1000
|
||||||
32
app_common/hooks.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Created on 2018-10-12
|
||||||
|
# author: 广州尚鹏,https://www.sunpop.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of Sunpop
|
||||||
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||||
|
|
||||||
|
# Odoo在线中文用户手册(长期更新)
|
||||||
|
# https://www.sunpop.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# https://www.sunpop.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# https://www.sunpop.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
from odoo import api, SUPERUSER_ID, _
|
||||||
|
|
||||||
|
|
||||||
|
def pre_init_hook(cr):
|
||||||
|
pass
|
||||||
|
# cr.execute("")
|
||||||
|
|
||||||
|
def post_init_hook(cr, registry):
|
||||||
|
pass
|
||||||
|
# cr.execute("")
|
||||||
|
|
||||||
|
def uninstall_hook(cr, registry):
|
||||||
|
pass
|
||||||
|
# cr.execute("")
|
||||||
|
|
||||||
29
app_common/i18n/zh_CN.po
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Translation of Odoo Server.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 10.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2018-01-08 14:28+0000\n"
|
||||||
|
"PO-Revision-Date: 2018-01-08 14:28+0000\n"
|
||||||
|
"Last-Translator: <>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: app_common
|
||||||
|
#: model:ir.model.fields,field_description:app_common.field_ir_cron__trigger_user_id
|
||||||
|
msgid "Last Trigger User"
|
||||||
|
msgstr "手动运行用户"
|
||||||
|
|
||||||
|
#. module: app_common
|
||||||
|
#: model:ir.model,name:app_common.model_ir_cron
|
||||||
|
msgid "Scheduled Actions"
|
||||||
|
msgstr "安排的动作"
|
||||||
|
|
||||||
|
#. module: app_common
|
||||||
|
#: model:ir.model,name:app_common.model_ir_ui_view
|
||||||
|
msgid "View"
|
||||||
|
msgstr "查看"
|
||||||
1
app_common/lib/ua_parser/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VERSION = (0, 10, 0)
|
||||||
7406
app_common/lib/ua_parser/_regexes.py
Normal file
544
app_common/lib/ua_parser/user_agent_parser.py
Normal file
@@ -0,0 +1,544 @@
|
|||||||
|
# Copyright 2009 Google Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the 'License')
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Python implementation of the UA parser."""
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
__author__ = "Lindsey Simon <elsigh@gmail.com>"
|
||||||
|
|
||||||
|
|
||||||
|
class UserAgentParser(object):
|
||||||
|
def __init__(
|
||||||
|
self, pattern, family_replacement=None, v1_replacement=None, v2_replacement=None
|
||||||
|
):
|
||||||
|
"""Initialize UserAgentParser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: a regular expression string
|
||||||
|
family_replacement: a string to override the matched family (optional)
|
||||||
|
v1_replacement: a string to override the matched v1 (optional)
|
||||||
|
v2_replacement: a string to override the matched v2 (optional)
|
||||||
|
"""
|
||||||
|
self.pattern = pattern
|
||||||
|
self.user_agent_re = re.compile(self.pattern)
|
||||||
|
self.family_replacement = family_replacement
|
||||||
|
self.v1_replacement = v1_replacement
|
||||||
|
self.v2_replacement = v2_replacement
|
||||||
|
|
||||||
|
def MatchSpans(self, user_agent_string):
|
||||||
|
match_spans = []
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
match_spans = [
|
||||||
|
match.span(group_index) for group_index in range(1, match.lastindex + 1)
|
||||||
|
]
|
||||||
|
return match_spans
|
||||||
|
|
||||||
|
def Parse(self, user_agent_string):
|
||||||
|
family, v1, v2, v3 = None, None, None, None
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
if self.family_replacement:
|
||||||
|
if re.search(r"\$1", self.family_replacement):
|
||||||
|
family = re.sub(r"\$1", match.group(1), self.family_replacement)
|
||||||
|
else:
|
||||||
|
family = self.family_replacement
|
||||||
|
else:
|
||||||
|
family = match.group(1)
|
||||||
|
|
||||||
|
if self.v1_replacement:
|
||||||
|
v1 = self.v1_replacement
|
||||||
|
elif match.lastindex and match.lastindex >= 2:
|
||||||
|
v1 = match.group(2) or None
|
||||||
|
|
||||||
|
if self.v2_replacement:
|
||||||
|
v2 = self.v2_replacement
|
||||||
|
elif match.lastindex and match.lastindex >= 3:
|
||||||
|
v2 = match.group(3) or None
|
||||||
|
|
||||||
|
if match.lastindex and match.lastindex >= 4:
|
||||||
|
v3 = match.group(4) or None
|
||||||
|
|
||||||
|
return family, v1, v2, v3
|
||||||
|
|
||||||
|
|
||||||
|
class OSParser(object):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pattern,
|
||||||
|
os_replacement=None,
|
||||||
|
os_v1_replacement=None,
|
||||||
|
os_v2_replacement=None,
|
||||||
|
os_v3_replacement=None,
|
||||||
|
os_v4_replacement=None,
|
||||||
|
):
|
||||||
|
"""Initialize UserAgentParser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: a regular expression string
|
||||||
|
os_replacement: a string to override the matched os (optional)
|
||||||
|
os_v1_replacement: a string to override the matched v1 (optional)
|
||||||
|
os_v2_replacement: a string to override the matched v2 (optional)
|
||||||
|
os_v3_replacement: a string to override the matched v3 (optional)
|
||||||
|
os_v4_replacement: a string to override the matched v4 (optional)
|
||||||
|
"""
|
||||||
|
self.pattern = pattern
|
||||||
|
self.user_agent_re = re.compile(self.pattern)
|
||||||
|
self.os_replacement = os_replacement
|
||||||
|
self.os_v1_replacement = os_v1_replacement
|
||||||
|
self.os_v2_replacement = os_v2_replacement
|
||||||
|
self.os_v3_replacement = os_v3_replacement
|
||||||
|
self.os_v4_replacement = os_v4_replacement
|
||||||
|
|
||||||
|
def MatchSpans(self, user_agent_string):
|
||||||
|
match_spans = []
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
match_spans = [
|
||||||
|
match.span(group_index) for group_index in range(1, match.lastindex + 1)
|
||||||
|
]
|
||||||
|
return match_spans
|
||||||
|
|
||||||
|
def Parse(self, user_agent_string):
|
||||||
|
os, os_v1, os_v2, os_v3, os_v4 = None, None, None, None, None
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
if self.os_replacement:
|
||||||
|
os = MultiReplace(self.os_replacement, match)
|
||||||
|
elif match.lastindex:
|
||||||
|
os = match.group(1)
|
||||||
|
|
||||||
|
if self.os_v1_replacement:
|
||||||
|
os_v1 = MultiReplace(self.os_v1_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 2:
|
||||||
|
os_v1 = match.group(2)
|
||||||
|
|
||||||
|
if self.os_v2_replacement:
|
||||||
|
os_v2 = MultiReplace(self.os_v2_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 3:
|
||||||
|
os_v2 = match.group(3)
|
||||||
|
|
||||||
|
if self.os_v3_replacement:
|
||||||
|
os_v3 = MultiReplace(self.os_v3_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 4:
|
||||||
|
os_v3 = match.group(4)
|
||||||
|
|
||||||
|
if self.os_v4_replacement:
|
||||||
|
os_v4 = MultiReplace(self.os_v4_replacement, match)
|
||||||
|
elif match.lastindex and match.lastindex >= 5:
|
||||||
|
os_v4 = match.group(5)
|
||||||
|
|
||||||
|
return os, os_v1, os_v2, os_v3, os_v4
|
||||||
|
|
||||||
|
|
||||||
|
def MultiReplace(string, match):
|
||||||
|
def _repl(m):
|
||||||
|
index = int(m.group(1)) - 1
|
||||||
|
group = match.groups()
|
||||||
|
if index < len(group):
|
||||||
|
return group[index]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
_string = re.sub(r"\$(\d)", _repl, string)
|
||||||
|
_string = re.sub(r"^\s+|\s+$", "", _string)
|
||||||
|
if _string == "":
|
||||||
|
return None
|
||||||
|
return _string
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceParser(object):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
pattern,
|
||||||
|
regex_flag=None,
|
||||||
|
device_replacement=None,
|
||||||
|
brand_replacement=None,
|
||||||
|
model_replacement=None,
|
||||||
|
):
|
||||||
|
"""Initialize UserAgentParser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pattern: a regular expression string
|
||||||
|
device_replacement: a string to override the matched device (optional)
|
||||||
|
"""
|
||||||
|
self.pattern = pattern
|
||||||
|
if regex_flag == "i":
|
||||||
|
self.user_agent_re = re.compile(self.pattern, re.IGNORECASE)
|
||||||
|
else:
|
||||||
|
self.user_agent_re = re.compile(self.pattern)
|
||||||
|
self.device_replacement = device_replacement
|
||||||
|
self.brand_replacement = brand_replacement
|
||||||
|
self.model_replacement = model_replacement
|
||||||
|
|
||||||
|
def MatchSpans(self, user_agent_string):
|
||||||
|
match_spans = []
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
match_spans = [
|
||||||
|
match.span(group_index) for group_index in range(1, match.lastindex + 1)
|
||||||
|
]
|
||||||
|
return match_spans
|
||||||
|
|
||||||
|
def Parse(self, user_agent_string):
|
||||||
|
device, brand, model = None, None, None
|
||||||
|
match = self.user_agent_re.search(user_agent_string)
|
||||||
|
if match:
|
||||||
|
if self.device_replacement:
|
||||||
|
device = MultiReplace(self.device_replacement, match)
|
||||||
|
else:
|
||||||
|
device = match.group(1)
|
||||||
|
|
||||||
|
if self.brand_replacement:
|
||||||
|
brand = MultiReplace(self.brand_replacement, match)
|
||||||
|
|
||||||
|
if self.model_replacement:
|
||||||
|
model = MultiReplace(self.model_replacement, match)
|
||||||
|
elif len(match.groups()) > 0:
|
||||||
|
model = match.group(1)
|
||||||
|
|
||||||
|
return device, brand, model
|
||||||
|
|
||||||
|
|
||||||
|
MAX_CACHE_SIZE = 20
|
||||||
|
_parse_cache = {}
|
||||||
|
|
||||||
|
|
||||||
|
def Parse(user_agent_string, **jsParseBits):
|
||||||
|
""" Parse all the things
|
||||||
|
Args:
|
||||||
|
user_agent_string: the full user agent string
|
||||||
|
jsParseBits: javascript override bits
|
||||||
|
Returns:
|
||||||
|
A dictionary containing all parsed bits
|
||||||
|
"""
|
||||||
|
jsParseBits = jsParseBits or {}
|
||||||
|
key = (user_agent_string, repr(jsParseBits))
|
||||||
|
cached = _parse_cache.get(key)
|
||||||
|
if cached is not None:
|
||||||
|
return cached
|
||||||
|
if len(_parse_cache) > MAX_CACHE_SIZE:
|
||||||
|
_parse_cache.clear()
|
||||||
|
v = {
|
||||||
|
"user_agent": ParseUserAgent(user_agent_string, **jsParseBits),
|
||||||
|
"os": ParseOS(user_agent_string, **jsParseBits),
|
||||||
|
"device": ParseDevice(user_agent_string, **jsParseBits),
|
||||||
|
"string": user_agent_string,
|
||||||
|
}
|
||||||
|
_parse_cache[key] = v
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def ParseUserAgent(user_agent_string, **jsParseBits):
|
||||||
|
""" Parses the user-agent string for user agent (browser) info.
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
jsParseBits: javascript override bits.
|
||||||
|
Returns:
|
||||||
|
A dictionary containing parsed bits.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
"js_user_agent_family" in jsParseBits
|
||||||
|
and jsParseBits["js_user_agent_family"] != ""
|
||||||
|
):
|
||||||
|
family = jsParseBits["js_user_agent_family"]
|
||||||
|
v1 = jsParseBits.get("js_user_agent_v1") or None
|
||||||
|
v2 = jsParseBits.get("js_user_agent_v2") or None
|
||||||
|
v3 = jsParseBits.get("js_user_agent_v3") or None
|
||||||
|
else:
|
||||||
|
for uaParser in USER_AGENT_PARSERS:
|
||||||
|
family, v1, v2, v3 = uaParser.Parse(user_agent_string)
|
||||||
|
if family:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Override for Chrome Frame IFF Chrome is enabled.
|
||||||
|
if "js_user_agent_string" in jsParseBits:
|
||||||
|
js_user_agent_string = jsParseBits["js_user_agent_string"]
|
||||||
|
if (
|
||||||
|
js_user_agent_string
|
||||||
|
and js_user_agent_string.find("Chrome/") > -1
|
||||||
|
and user_agent_string.find("chromeframe") > -1
|
||||||
|
):
|
||||||
|
jsOverride = {}
|
||||||
|
jsOverride = ParseUserAgent(js_user_agent_string)
|
||||||
|
family = "Chrome Frame (%s %s)" % (family, v1)
|
||||||
|
v1 = jsOverride["major"]
|
||||||
|
v2 = jsOverride["minor"]
|
||||||
|
v3 = jsOverride["patch"]
|
||||||
|
|
||||||
|
family = family or "Other"
|
||||||
|
return {
|
||||||
|
"family": family,
|
||||||
|
"major": v1 or None,
|
||||||
|
"minor": v2 or None,
|
||||||
|
"patch": v3 or None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def ParseOS(user_agent_string, **jsParseBits):
|
||||||
|
""" Parses the user-agent string for operating system info
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
jsParseBits: javascript override bits.
|
||||||
|
Returns:
|
||||||
|
A dictionary containing parsed bits.
|
||||||
|
"""
|
||||||
|
for osParser in OS_PARSERS:
|
||||||
|
os, os_v1, os_v2, os_v3, os_v4 = osParser.Parse(user_agent_string)
|
||||||
|
if os:
|
||||||
|
break
|
||||||
|
os = os or "Other"
|
||||||
|
return {
|
||||||
|
"family": os,
|
||||||
|
"major": os_v1,
|
||||||
|
"minor": os_v2,
|
||||||
|
"patch": os_v3,
|
||||||
|
"patch_minor": os_v4,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def ParseDevice(user_agent_string):
|
||||||
|
""" Parses the user-agent string for device info.
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
ua_family: The parsed user agent family name.
|
||||||
|
Returns:
|
||||||
|
A dictionary containing parsed bits.
|
||||||
|
"""
|
||||||
|
for deviceParser in DEVICE_PARSERS:
|
||||||
|
device, brand, model = deviceParser.Parse(user_agent_string)
|
||||||
|
if device:
|
||||||
|
break
|
||||||
|
|
||||||
|
if device is None:
|
||||||
|
device = "Other"
|
||||||
|
|
||||||
|
return {"family": device, "brand": brand, "model": model}
|
||||||
|
|
||||||
|
|
||||||
|
def PrettyUserAgent(family, v1=None, v2=None, v3=None):
|
||||||
|
"""Pretty user agent string."""
|
||||||
|
if v3:
|
||||||
|
if v3[0].isdigit():
|
||||||
|
return "%s %s.%s.%s" % (family, v1, v2, v3)
|
||||||
|
else:
|
||||||
|
return "%s %s.%s%s" % (family, v1, v2, v3)
|
||||||
|
elif v2:
|
||||||
|
return "%s %s.%s" % (family, v1, v2)
|
||||||
|
elif v1:
|
||||||
|
return "%s %s" % (family, v1)
|
||||||
|
return family
|
||||||
|
|
||||||
|
|
||||||
|
def PrettyOS(os, os_v1=None, os_v2=None, os_v3=None, os_v4=None):
|
||||||
|
"""Pretty os string."""
|
||||||
|
if os_v4:
|
||||||
|
return "%s %s.%s.%s.%s" % (os, os_v1, os_v2, os_v3, os_v4)
|
||||||
|
if os_v3:
|
||||||
|
if os_v3[0].isdigit():
|
||||||
|
return "%s %s.%s.%s" % (os, os_v1, os_v2, os_v3)
|
||||||
|
else:
|
||||||
|
return "%s %s.%s%s" % (os, os_v1, os_v2, os_v3)
|
||||||
|
elif os_v2:
|
||||||
|
return "%s %s.%s" % (os, os_v1, os_v2)
|
||||||
|
elif os_v1:
|
||||||
|
return "%s %s" % (os, os_v1)
|
||||||
|
return os
|
||||||
|
|
||||||
|
|
||||||
|
def ParseWithJSOverrides(
|
||||||
|
user_agent_string,
|
||||||
|
js_user_agent_string=None,
|
||||||
|
js_user_agent_family=None,
|
||||||
|
js_user_agent_v1=None,
|
||||||
|
js_user_agent_v2=None,
|
||||||
|
js_user_agent_v3=None,
|
||||||
|
):
|
||||||
|
""" backwards compatible. use one of the other Parse methods instead! """
|
||||||
|
|
||||||
|
# Override via JS properties.
|
||||||
|
if js_user_agent_family is not None and js_user_agent_family != "":
|
||||||
|
family = js_user_agent_family
|
||||||
|
v1 = None
|
||||||
|
v2 = None
|
||||||
|
v3 = None
|
||||||
|
if js_user_agent_v1 is not None:
|
||||||
|
v1 = js_user_agent_v1
|
||||||
|
if js_user_agent_v2 is not None:
|
||||||
|
v2 = js_user_agent_v2
|
||||||
|
if js_user_agent_v3 is not None:
|
||||||
|
v3 = js_user_agent_v3
|
||||||
|
else:
|
||||||
|
for parser in USER_AGENT_PARSERS:
|
||||||
|
family, v1, v2, v3 = parser.Parse(user_agent_string)
|
||||||
|
if family:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Override for Chrome Frame IFF Chrome is enabled.
|
||||||
|
if (
|
||||||
|
js_user_agent_string
|
||||||
|
and js_user_agent_string.find("Chrome/") > -1
|
||||||
|
and user_agent_string.find("chromeframe") > -1
|
||||||
|
):
|
||||||
|
family = "Chrome Frame (%s %s)" % (family, v1)
|
||||||
|
ua_dict = ParseUserAgent(js_user_agent_string)
|
||||||
|
v1 = ua_dict["major"]
|
||||||
|
v2 = ua_dict["minor"]
|
||||||
|
v3 = ua_dict["patch"]
|
||||||
|
|
||||||
|
return family or "Other", v1, v2, v3
|
||||||
|
|
||||||
|
|
||||||
|
def Pretty(family, v1=None, v2=None, v3=None):
|
||||||
|
""" backwards compatible. use PrettyUserAgent instead! """
|
||||||
|
if v3:
|
||||||
|
if v3[0].isdigit():
|
||||||
|
return "%s %s.%s.%s" % (family, v1, v2, v3)
|
||||||
|
else:
|
||||||
|
return "%s %s.%s%s" % (family, v1, v2, v3)
|
||||||
|
elif v2:
|
||||||
|
return "%s %s.%s" % (family, v1, v2)
|
||||||
|
elif v1:
|
||||||
|
return "%s %s" % (family, v1)
|
||||||
|
return family
|
||||||
|
|
||||||
|
|
||||||
|
def GetFilters(
|
||||||
|
user_agent_string,
|
||||||
|
js_user_agent_string=None,
|
||||||
|
js_user_agent_family=None,
|
||||||
|
js_user_agent_v1=None,
|
||||||
|
js_user_agent_v2=None,
|
||||||
|
js_user_agent_v3=None,
|
||||||
|
):
|
||||||
|
"""Return the optional arguments that should be saved and used to query.
|
||||||
|
|
||||||
|
js_user_agent_string is always returned if it is present. We really only need
|
||||||
|
it for Chrome Frame. However, I added it in the generally case to find other
|
||||||
|
cases when it is different. When the recording of js_user_agent_string was
|
||||||
|
added, we created new records for all new user agents.
|
||||||
|
|
||||||
|
Since we only added js_document_mode for the IE 9 preview case, it did not
|
||||||
|
cause new user agent records the way js_user_agent_string did.
|
||||||
|
|
||||||
|
js_document_mode has since been removed in favor of individual property
|
||||||
|
overrides.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_agent_string: The full user-agent string.
|
||||||
|
js_user_agent_string: JavaScript ua string from client-side
|
||||||
|
js_user_agent_family: This is an override for the family name to deal
|
||||||
|
with the fact that IE platform preview (for instance) cannot be
|
||||||
|
distinguished by user_agent_string, but only in javascript.
|
||||||
|
js_user_agent_v1: v1 override - see above.
|
||||||
|
js_user_agent_v2: v1 override - see above.
|
||||||
|
js_user_agent_v3: v1 override - see above.
|
||||||
|
Returns:
|
||||||
|
{js_user_agent_string: '[...]', js_family_name: '[...]', etc...}
|
||||||
|
"""
|
||||||
|
filters = {}
|
||||||
|
filterdict = {
|
||||||
|
"js_user_agent_string": js_user_agent_string,
|
||||||
|
"js_user_agent_family": js_user_agent_family,
|
||||||
|
"js_user_agent_v1": js_user_agent_v1,
|
||||||
|
"js_user_agent_v2": js_user_agent_v2,
|
||||||
|
"js_user_agent_v3": js_user_agent_v3,
|
||||||
|
}
|
||||||
|
for key, value in filterdict.items():
|
||||||
|
if value is not None and value != "":
|
||||||
|
filters[key] = value
|
||||||
|
return filters
|
||||||
|
|
||||||
|
|
||||||
|
# Build the list of user agent parsers from YAML
|
||||||
|
UA_PARSER_YAML = os.environ.get("UA_PARSER_YAML")
|
||||||
|
if UA_PARSER_YAML:
|
||||||
|
# This will raise an ImportError if missing, obviously since it's no
|
||||||
|
# longer a requirement
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try and use libyaml bindings if available since faster
|
||||||
|
from yaml import CSafeLoader as SafeLoader
|
||||||
|
except ImportError:
|
||||||
|
from yaml import SafeLoader
|
||||||
|
|
||||||
|
with open(UA_PARSER_YAML) as fp:
|
||||||
|
regexes = yaml.load(fp, Loader=SafeLoader)
|
||||||
|
|
||||||
|
USER_AGENT_PARSERS = []
|
||||||
|
for _ua_parser in regexes["user_agent_parsers"]:
|
||||||
|
_regex = _ua_parser["regex"]
|
||||||
|
|
||||||
|
_family_replacement = _ua_parser.get("family_replacement")
|
||||||
|
_v1_replacement = _ua_parser.get("v1_replacement")
|
||||||
|
_v2_replacement = _ua_parser.get("v2_replacement")
|
||||||
|
|
||||||
|
USER_AGENT_PARSERS.append(
|
||||||
|
UserAgentParser(
|
||||||
|
_regex, _family_replacement, _v1_replacement, _v2_replacement
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
OS_PARSERS = []
|
||||||
|
for _os_parser in regexes["os_parsers"]:
|
||||||
|
_regex = _os_parser["regex"]
|
||||||
|
|
||||||
|
_os_replacement = _os_parser.get("os_replacement")
|
||||||
|
_os_v1_replacement = _os_parser.get("os_v1_replacement")
|
||||||
|
_os_v2_replacement = _os_parser.get("os_v2_replacement")
|
||||||
|
_os_v3_replacement = _os_parser.get("os_v3_replacement")
|
||||||
|
_os_v4_replacement = _os_parser.get("os_v4_replacement")
|
||||||
|
|
||||||
|
OS_PARSERS.append(
|
||||||
|
OSParser(
|
||||||
|
_regex,
|
||||||
|
_os_replacement,
|
||||||
|
_os_v1_replacement,
|
||||||
|
_os_v2_replacement,
|
||||||
|
_os_v3_replacement,
|
||||||
|
_os_v4_replacement,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
DEVICE_PARSERS = []
|
||||||
|
for _device_parser in regexes["device_parsers"]:
|
||||||
|
_regex = _device_parser["regex"]
|
||||||
|
|
||||||
|
_regex_flag = _device_parser.get("regex_flag")
|
||||||
|
_device_replacement = _device_parser.get("device_replacement")
|
||||||
|
_brand_replacement = _device_parser.get("brand_replacement")
|
||||||
|
_model_replacement = _device_parser.get("model_replacement")
|
||||||
|
|
||||||
|
DEVICE_PARSERS.append(
|
||||||
|
DeviceParser(
|
||||||
|
_regex,
|
||||||
|
_regex_flag,
|
||||||
|
_device_replacement,
|
||||||
|
_brand_replacement,
|
||||||
|
_model_replacement,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Clean our our temporary vars explicitly
|
||||||
|
# so they can't be reused or imported
|
||||||
|
del regexes
|
||||||
|
del yaml
|
||||||
|
del SafeLoader
|
||||||
|
else:
|
||||||
|
# Just load our pre-compiled versions
|
||||||
|
from ._regexes import USER_AGENT_PARSERS, DEVICE_PARSERS, OS_PARSERS
|
||||||
290
app_common/lib/ua_parser/user_agent_parser_test.py
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
#!/usr/bin/python2.5
|
||||||
|
#
|
||||||
|
# Copyright 2008 Google Inc.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the 'License')
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
"""User Agent Parser Unit Tests.
|
||||||
|
Run:
|
||||||
|
# python -m user_agent_parser_test (runs all the tests, takes awhile)
|
||||||
|
or like:
|
||||||
|
# python -m user_agent_parser_test ParseTest.testBrowserscopeStrings
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
__author__ = "slamm@google.com (Stephen Lamm)"
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import unittest
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try and use libyaml bindings if available since faster
|
||||||
|
from yaml import CSafeLoader as SafeLoader
|
||||||
|
except ImportError:
|
||||||
|
from yaml import SafeLoader
|
||||||
|
|
||||||
|
from ua_parser import user_agent_parser
|
||||||
|
|
||||||
|
TEST_RESOURCES_DIR = os.path.join(
|
||||||
|
os.path.abspath(os.path.dirname(__file__)), "../uap-core"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ParseTest(unittest.TestCase):
|
||||||
|
def testBrowserscopeStrings(self):
|
||||||
|
self.runUserAgentTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "tests/test_ua.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testBrowserscopeStringsOS(self):
|
||||||
|
self.runOSTestsFromYAML(os.path.join(TEST_RESOURCES_DIR, "tests/test_os.yaml"))
|
||||||
|
|
||||||
|
def testStringsOS(self):
|
||||||
|
self.runOSTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "test_resources/additional_os_tests.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testStringsDevice(self):
|
||||||
|
self.runDeviceTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "tests/test_device.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testMozillaStrings(self):
|
||||||
|
self.runUserAgentTestsFromYAML(
|
||||||
|
os.path.join(
|
||||||
|
TEST_RESOURCES_DIR, "test_resources/firefox_user_agent_strings.yaml"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# NOTE: The YAML file used here is one output by makePGTSComparisonYAML()
|
||||||
|
# below, as opposed to the pgts_browser_list-orig.yaml file. The -orig
|
||||||
|
# file is by no means perfect, but identifies many browsers that we
|
||||||
|
# classify as "Other". This test itself is mostly useful to know when
|
||||||
|
# somthing in UA parsing changes. An effort should be made to try and
|
||||||
|
# reconcile the differences between the two YAML files.
|
||||||
|
def testPGTSStrings(self):
|
||||||
|
self.runUserAgentTestsFromYAML(
|
||||||
|
os.path.join(TEST_RESOURCES_DIR, "test_resources/pgts_browser_list.yaml")
|
||||||
|
)
|
||||||
|
|
||||||
|
def testParseAll(self):
|
||||||
|
user_agent_string = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; fr; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5,gzip(gfe),gzip(gfe)"
|
||||||
|
expected = {
|
||||||
|
"device": {"family": "Mac", "brand": "Apple", "model": "Mac"},
|
||||||
|
"os": {
|
||||||
|
"family": "Mac OS X",
|
||||||
|
"major": "10",
|
||||||
|
"minor": "4",
|
||||||
|
"patch": None,
|
||||||
|
"patch_minor": None,
|
||||||
|
},
|
||||||
|
"user_agent": {
|
||||||
|
"family": "Firefox",
|
||||||
|
"major": "3",
|
||||||
|
"minor": "5",
|
||||||
|
"patch": "5",
|
||||||
|
},
|
||||||
|
"string": user_agent_string,
|
||||||
|
}
|
||||||
|
|
||||||
|
result = user_agent_parser.Parse(user_agent_string)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1}> != actual<{2}>".format(
|
||||||
|
user_agent_string, expected, result
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a YAML file for manual comparsion with pgts_browser_list-orig.yaml
|
||||||
|
def makePGTSComparisonYAML(self):
|
||||||
|
import codecs
|
||||||
|
|
||||||
|
outfile = codecs.open("outfile.yaml", "w", "utf-8")
|
||||||
|
print >> outfile, "test_cases:"
|
||||||
|
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, "pgts_browser_list.yaml"))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
(family, major, minor, patch) = user_agent_parser.ParseUserAgent(
|
||||||
|
user_agent_string, **kwds
|
||||||
|
)
|
||||||
|
|
||||||
|
# Escape any double-quotes in the UA string
|
||||||
|
user_agent_string = re.sub(r'"', '\\"', user_agent_string)
|
||||||
|
print >> outfile, ' - user_agent_string: "' + user_agent_string + '"' + "\n" + ' family: "' + family + '"\n' + " major: " + (
|
||||||
|
"" if (major is None) else "'" + major + "'"
|
||||||
|
) + "\n" + " minor: " + (
|
||||||
|
"" if (minor is None) else "'" + minor + "'"
|
||||||
|
) + "\n" + " patch: " + (
|
||||||
|
"" if (patch is None) else "'" + patch + "'"
|
||||||
|
)
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
|
# Run a set of test cases from a YAML file
|
||||||
|
def runUserAgentTestsFromYAML(self, file_name):
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
# Inputs to Parse()
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
# The expected results
|
||||||
|
expected = {
|
||||||
|
"family": test_case["family"],
|
||||||
|
"major": test_case["major"],
|
||||||
|
"minor": test_case["minor"],
|
||||||
|
"patch": test_case["patch"],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result = user_agent_parser.ParseUserAgent(user_agent_string, **kwds)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1}, {2}, {3}, {4}> != actual<{5}, {6}, {7}, {8}>".format(
|
||||||
|
user_agent_string,
|
||||||
|
expected["family"],
|
||||||
|
expected["major"],
|
||||||
|
expected["minor"],
|
||||||
|
expected["patch"],
|
||||||
|
result["family"],
|
||||||
|
result["major"],
|
||||||
|
result["minor"],
|
||||||
|
result["patch"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def runOSTestsFromYAML(self, file_name):
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
# Inputs to Parse()
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
# The expected results
|
||||||
|
expected = {
|
||||||
|
"family": test_case["family"],
|
||||||
|
"major": test_case["major"],
|
||||||
|
"minor": test_case["minor"],
|
||||||
|
"patch": test_case["patch"],
|
||||||
|
"patch_minor": test_case["patch_minor"],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = user_agent_parser.ParseOS(user_agent_string, **kwds)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1} {2} {3} {4} {5}> != actual<{6} {7} {8} {9} {10}>".format(
|
||||||
|
user_agent_string,
|
||||||
|
expected["family"],
|
||||||
|
expected["major"],
|
||||||
|
expected["minor"],
|
||||||
|
expected["patch"],
|
||||||
|
expected["patch_minor"],
|
||||||
|
result["family"],
|
||||||
|
result["major"],
|
||||||
|
result["minor"],
|
||||||
|
result["patch"],
|
||||||
|
result["patch_minor"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def runDeviceTestsFromYAML(self, file_name):
|
||||||
|
yamlFile = open(os.path.join(TEST_RESOURCES_DIR, file_name))
|
||||||
|
yamlContents = yaml.load(yamlFile, Loader=SafeLoader)
|
||||||
|
yamlFile.close()
|
||||||
|
|
||||||
|
for test_case in yamlContents["test_cases"]:
|
||||||
|
# Inputs to Parse()
|
||||||
|
user_agent_string = test_case["user_agent_string"]
|
||||||
|
kwds = {}
|
||||||
|
if "js_ua" in test_case:
|
||||||
|
kwds = eval(test_case["js_ua"])
|
||||||
|
|
||||||
|
# The expected results
|
||||||
|
expected = {
|
||||||
|
"family": test_case["family"],
|
||||||
|
"brand": test_case["brand"],
|
||||||
|
"model": test_case["model"],
|
||||||
|
}
|
||||||
|
|
||||||
|
result = user_agent_parser.ParseDevice(user_agent_string, **kwds)
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
expected,
|
||||||
|
"UA: {0}\n expected<{1} {2} {3}> != actual<{4} {5} {6}>".format(
|
||||||
|
user_agent_string,
|
||||||
|
expected["family"],
|
||||||
|
expected["brand"],
|
||||||
|
expected["model"],
|
||||||
|
result["family"],
|
||||||
|
result["brand"],
|
||||||
|
result["model"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GetFiltersTest(unittest.TestCase):
|
||||||
|
def testGetFiltersNoMatchesGiveEmptyDict(self):
|
||||||
|
user_agent_string = "foo"
|
||||||
|
filters = user_agent_parser.GetFilters(
|
||||||
|
user_agent_string, js_user_agent_string=None
|
||||||
|
)
|
||||||
|
self.assertEqual({}, filters)
|
||||||
|
|
||||||
|
def testGetFiltersJsUaPassedThrough(self):
|
||||||
|
user_agent_string = "foo"
|
||||||
|
filters = user_agent_parser.GetFilters(
|
||||||
|
user_agent_string, js_user_agent_string="bar"
|
||||||
|
)
|
||||||
|
self.assertEqual({"js_user_agent_string": "bar"}, filters)
|
||||||
|
|
||||||
|
def testGetFiltersJsUserAgentFamilyAndVersions(self):
|
||||||
|
user_agent_string = (
|
||||||
|
"Mozilla/4.0 (compatible; MSIE 8.0; "
|
||||||
|
"Windows NT 5.1; Trident/4.0; GTB6; .NET CLR 2.0.50727; "
|
||||||
|
".NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"
|
||||||
|
)
|
||||||
|
filters = user_agent_parser.GetFilters(
|
||||||
|
user_agent_string, js_user_agent_string="bar", js_user_agent_family="foo"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
{"js_user_agent_string": "bar", "js_user_agent_family": "foo"}, filters
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
3
app_common/lib/user_agents/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
VERSION = (2, 2, 0)
|
||||||
|
|
||||||
|
from .parsers import parse
|
||||||
14
app_common/lib/user_agents/compat.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
PY3 = sys.version_info[0] == 3
|
||||||
|
|
||||||
|
if PY3:
|
||||||
|
string_types = str
|
||||||
|
|
||||||
|
def iteritems(d, **kw):
|
||||||
|
return iter(d.items(**kw))
|
||||||
|
else:
|
||||||
|
string_types = basestring
|
||||||
|
|
||||||
|
def iteritems(d, **kw):
|
||||||
|
return iter(d.iteritems(**kw))
|
||||||
268
app_common/lib/user_agents/parsers.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from ..ua_parser import user_agent_parser
|
||||||
|
from .compat import string_types
|
||||||
|
|
||||||
|
|
||||||
|
MOBILE_DEVICE_FAMILIES = (
|
||||||
|
'iPhone',
|
||||||
|
'iPod',
|
||||||
|
'Generic Smartphone',
|
||||||
|
'Generic Feature Phone',
|
||||||
|
'PlayStation Vita',
|
||||||
|
'iOS-Device'
|
||||||
|
)
|
||||||
|
|
||||||
|
PC_OS_FAMILIES = (
|
||||||
|
'Windows 95',
|
||||||
|
'Windows 98',
|
||||||
|
'Solaris',
|
||||||
|
)
|
||||||
|
|
||||||
|
MOBILE_OS_FAMILIES = (
|
||||||
|
'Windows Phone',
|
||||||
|
'Windows Phone OS', # Earlier versions of ua-parser returns Windows Phone OS
|
||||||
|
'Symbian OS',
|
||||||
|
'Bada',
|
||||||
|
'Windows CE',
|
||||||
|
'Windows Mobile',
|
||||||
|
'Maemo',
|
||||||
|
)
|
||||||
|
|
||||||
|
MOBILE_BROWSER_FAMILIES = (
|
||||||
|
'IE Mobile',
|
||||||
|
'Opera Mobile',
|
||||||
|
'Opera Mini',
|
||||||
|
'Chrome Mobile',
|
||||||
|
'Chrome Mobile WebView',
|
||||||
|
'Chrome Mobile iOS',
|
||||||
|
)
|
||||||
|
|
||||||
|
TABLET_DEVICE_FAMILIES = (
|
||||||
|
'iPad',
|
||||||
|
'BlackBerry Playbook',
|
||||||
|
'Blackberry Playbook', # Earlier versions of ua-parser returns "Blackberry" instead of "BlackBerry"
|
||||||
|
'Kindle',
|
||||||
|
'Kindle Fire',
|
||||||
|
'Kindle Fire HD',
|
||||||
|
'Galaxy Tab',
|
||||||
|
'Xoom',
|
||||||
|
'Dell Streak',
|
||||||
|
)
|
||||||
|
|
||||||
|
TOUCH_CAPABLE_OS_FAMILIES = (
|
||||||
|
'iOS',
|
||||||
|
'Android',
|
||||||
|
'Windows Phone',
|
||||||
|
'Windows CE',
|
||||||
|
'Windows Mobile',
|
||||||
|
'Firefox OS',
|
||||||
|
'MeeGo',
|
||||||
|
)
|
||||||
|
|
||||||
|
TOUCH_CAPABLE_DEVICE_FAMILIES = (
|
||||||
|
'BlackBerry Playbook',
|
||||||
|
'Blackberry Playbook',
|
||||||
|
'Kindle Fire',
|
||||||
|
)
|
||||||
|
|
||||||
|
EMAIL_PROGRAM_FAMILIES = set((
|
||||||
|
'Outlook',
|
||||||
|
'Windows Live Mail',
|
||||||
|
'AirMail',
|
||||||
|
'Apple Mail',
|
||||||
|
'Outlook',
|
||||||
|
'Thunderbird',
|
||||||
|
'Lightning',
|
||||||
|
'ThunderBrowse',
|
||||||
|
'Windows Live Mail',
|
||||||
|
'The Bat!',
|
||||||
|
'Lotus Notes',
|
||||||
|
'IBM Notes',
|
||||||
|
'Barca',
|
||||||
|
'MailBar',
|
||||||
|
'kmail2',
|
||||||
|
'YahooMobileMail'
|
||||||
|
))
|
||||||
|
|
||||||
|
def verify_attribute(attribute):
|
||||||
|
if isinstance(attribute, string_types) and attribute.isdigit():
|
||||||
|
return int(attribute)
|
||||||
|
|
||||||
|
return attribute
|
||||||
|
|
||||||
|
|
||||||
|
def parse_version(major=None, minor=None, patch=None, patch_minor=None):
|
||||||
|
# Returns version number tuple, attributes will be integer if they're numbers
|
||||||
|
major = verify_attribute(major)
|
||||||
|
minor = verify_attribute(minor)
|
||||||
|
patch = verify_attribute(patch)
|
||||||
|
patch_minor = verify_attribute(patch_minor)
|
||||||
|
|
||||||
|
return tuple(
|
||||||
|
filter(lambda x: x is not None, (major, minor, patch, patch_minor))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Browser = namedtuple('Browser', ['family', 'version', 'version_string'])
|
||||||
|
|
||||||
|
|
||||||
|
def parse_browser(family, major=None, minor=None, patch=None, patch_minor=None):
|
||||||
|
# Returns a browser object
|
||||||
|
version = parse_version(major, minor, patch)
|
||||||
|
version_string = '.'.join([str(v) for v in version])
|
||||||
|
return Browser(family, version, version_string)
|
||||||
|
|
||||||
|
|
||||||
|
OperatingSystem = namedtuple('OperatingSystem', ['family', 'version', 'version_string'])
|
||||||
|
|
||||||
|
|
||||||
|
def parse_operating_system(family, major=None, minor=None, patch=None, patch_minor=None):
|
||||||
|
version = parse_version(major, minor, patch)
|
||||||
|
version_string = '.'.join([str(v) for v in version])
|
||||||
|
return OperatingSystem(family, version, version_string)
|
||||||
|
|
||||||
|
|
||||||
|
Device = namedtuple('Device', ['family', 'brand', 'model'])
|
||||||
|
|
||||||
|
|
||||||
|
def parse_device(family, brand, model):
|
||||||
|
return Device(family, brand, model)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAgent(object):
|
||||||
|
|
||||||
|
def __init__(self, user_agent_string):
|
||||||
|
ua_dict = user_agent_parser.Parse(user_agent_string)
|
||||||
|
self.ua_string = user_agent_string
|
||||||
|
self.os = parse_operating_system(**ua_dict['os'])
|
||||||
|
self.browser = parse_browser(**ua_dict['user_agent'])
|
||||||
|
self.device = parse_device(**ua_dict['device'])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{device} / {os} / {browser}".format(
|
||||||
|
device=self.get_device(),
|
||||||
|
os=self.get_os(),
|
||||||
|
browser=self.get_browser()
|
||||||
|
)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(str(self))
|
||||||
|
|
||||||
|
def _is_android_tablet(self):
|
||||||
|
# Newer Android tablets don't have "Mobile" in their user agent string,
|
||||||
|
# older ones like Galaxy Tab still have "Mobile" though they're not
|
||||||
|
if ('Mobile Safari' not in self.ua_string and
|
||||||
|
self.browser.family != "Firefox Mobile"):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_blackberry_touch_capable_device(self):
|
||||||
|
# A helper to determine whether a BB phone has touch capabilities
|
||||||
|
# Blackberry Bold Touch series begins with 99XX
|
||||||
|
if 'Blackberry 99' in self.device.family:
|
||||||
|
return True
|
||||||
|
if 'Blackberry 95' in self.device.family: # BB Storm devices
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_device(self):
|
||||||
|
return self.is_pc and "PC" or self.device.family
|
||||||
|
|
||||||
|
def get_os(self):
|
||||||
|
return ("%s %s" % (self.os.family, self.os.version_string)).strip()
|
||||||
|
|
||||||
|
def get_browser(self):
|
||||||
|
return ("%s %s" % (self.browser.family, self.browser.version_string)).strip()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_tablet(self):
|
||||||
|
if self.device.family in TABLET_DEVICE_FAMILIES:
|
||||||
|
return True
|
||||||
|
if (self.os.family == 'Android' and self._is_android_tablet()):
|
||||||
|
return True
|
||||||
|
if self.os.family == 'Windows' and self.os.version_string.startswith('RT'):
|
||||||
|
return True
|
||||||
|
if self.os.family == 'Firefox OS' and 'Mobile' not in self.browser.family:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_mobile(self):
|
||||||
|
# First check for mobile device and mobile browser families
|
||||||
|
if self.device.family in MOBILE_DEVICE_FAMILIES:
|
||||||
|
return True
|
||||||
|
if self.browser.family in MOBILE_BROWSER_FAMILIES:
|
||||||
|
return True
|
||||||
|
# Device is considered Mobile OS is Android and not tablet
|
||||||
|
# This is not fool proof but would have to suffice for now
|
||||||
|
if ((self.os.family == 'Android' or self.os.family == 'Firefox OS')
|
||||||
|
and not self.is_tablet):
|
||||||
|
return True
|
||||||
|
if self.os.family == 'BlackBerry OS' and self.device.family != 'Blackberry Playbook':
|
||||||
|
return True
|
||||||
|
if self.os.family in MOBILE_OS_FAMILIES:
|
||||||
|
return True
|
||||||
|
# TODO: remove after https://github.com/tobie/ua-parser/issues/126 is closed
|
||||||
|
if 'J2ME' in self.ua_string or 'MIDP' in self.ua_string:
|
||||||
|
return True
|
||||||
|
# This is here mainly to detect Google's Mobile Spider
|
||||||
|
if 'iPhone;' in self.ua_string:
|
||||||
|
return True
|
||||||
|
if 'Googlebot-Mobile' in self.ua_string:
|
||||||
|
return True
|
||||||
|
# Mobile Spiders should be identified as mobile
|
||||||
|
if self.device.family == 'Spider' and 'Mobile' in self.browser.family:
|
||||||
|
return True
|
||||||
|
# Nokia mobile
|
||||||
|
if 'NokiaBrowser' in self.ua_string and 'Mobile' in self.ua_string:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_touch_capable(self):
|
||||||
|
# TODO: detect touch capable Nokia devices
|
||||||
|
if self.os.family in TOUCH_CAPABLE_OS_FAMILIES:
|
||||||
|
return True
|
||||||
|
if self.device.family in TOUCH_CAPABLE_DEVICE_FAMILIES:
|
||||||
|
return True
|
||||||
|
if self.os.family == 'Windows':
|
||||||
|
if self.os.version_string.startswith(('RT', 'CE')):
|
||||||
|
return True
|
||||||
|
if self.os.version_string.startswith('8') and 'Touch' in self.ua_string:
|
||||||
|
return True
|
||||||
|
if 'BlackBerry' in self.os.family and self._is_blackberry_touch_capable_device():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_pc(self):
|
||||||
|
# Returns True for "PC" devices (Windows, Mac and Linux)
|
||||||
|
if 'Windows NT' in self.ua_string or self.os.family in PC_OS_FAMILIES or \
|
||||||
|
self.os.family == 'Windows' and self.os.version_string == 'ME':
|
||||||
|
return True
|
||||||
|
# TODO: remove after https://github.com/tobie/ua-parser/issues/127 is closed
|
||||||
|
if self.os.family == 'Mac OS X' and 'Silk' not in self.ua_string:
|
||||||
|
return True
|
||||||
|
# Maemo has 'Linux' and 'X11' in UA, but it is not for PC
|
||||||
|
if 'Maemo' in self.ua_string:
|
||||||
|
return False
|
||||||
|
if 'Chrome OS' in self.os.family:
|
||||||
|
return True
|
||||||
|
if 'Linux' in self.ua_string and 'X11' in self.ua_string:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_bot(self):
|
||||||
|
return True if self.device.family == 'Spider' else False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_email_client(self):
|
||||||
|
if self.browser.family in EMAIL_PROGRAM_FAMILIES:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def parse(user_agent_string):
|
||||||
|
return UserAgent(user_agent_string)
|
||||||
268
app_common/lib/user_agents/tests.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from ua_parser import user_agent_parser
|
||||||
|
from . import compat
|
||||||
|
from .parsers import parse
|
||||||
|
|
||||||
|
|
||||||
|
iphone_ua_string = 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B179 Safari/7534.48.3'
|
||||||
|
ipad_ua_string = 'Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10'
|
||||||
|
galaxy_tab_ua_string = 'Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1'
|
||||||
|
galaxy_s3_ua_string = 'Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'
|
||||||
|
kindle_fire_ua_string = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-80) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true'
|
||||||
|
playbook_ua_string = 'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.0.1; en-US) AppleWebKit/535.8+ (KHTML, like Gecko) Version/7.2.0.1 Safari/535.8+'
|
||||||
|
nexus_7_ua_string = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'
|
||||||
|
windows_phone_ua_string = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; SGH-i917)'
|
||||||
|
blackberry_torch_ua_string = 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; zh-TW) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.448 Mobile Safari/534.8+'
|
||||||
|
blackberry_bold_ua_string = 'BlackBerry9700/5.0.0.862 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/331 UNTRUSTED/1.0 3gpp-gba'
|
||||||
|
blackberry_bold_touch_ua_string = 'Mozilla/5.0 (BlackBerry; U; BlackBerry 9930; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0.241 Mobile Safari/534.11+'
|
||||||
|
windows_rt_ua_string = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)'
|
||||||
|
j2me_opera_ua_string = 'Opera/9.80 (J2ME/MIDP; Opera Mini/9.80 (J2ME/22.478; U; en) Presto/2.5.25 Version/10.54'
|
||||||
|
ie_ua_string = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)'
|
||||||
|
ie_touch_ua_string = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Touch)'
|
||||||
|
mac_safari_ua_string = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2'
|
||||||
|
windows_ie_ua_string = 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
|
||||||
|
ubuntu_firefox_ua_string = 'Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1'
|
||||||
|
google_bot_ua_string = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
|
||||||
|
nokia_n97_ua_string = 'Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/12.0.024; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.12344'
|
||||||
|
android_firefox_aurora_ua_string = 'Mozilla/5.0 (Android; Mobile; rv:27.0) Gecko/27.0 Firefox/27.0'
|
||||||
|
thunderbird_ua_string = 'Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0 Lightning/4.0.2'
|
||||||
|
outlook_usa_string = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0; Microsoft Outlook 15.0.4420)'
|
||||||
|
chromebook_ua_string = 'Mozilla/5.0 (X11; CrOS i686 0.12.433) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.77 Safari/534.30'
|
||||||
|
|
||||||
|
iphone_ua = parse(iphone_ua_string)
|
||||||
|
ipad_ua = parse(ipad_ua_string)
|
||||||
|
galaxy_tab = parse(galaxy_tab_ua_string)
|
||||||
|
galaxy_s3_ua = parse(galaxy_s3_ua_string)
|
||||||
|
kindle_fire_ua = parse(kindle_fire_ua_string)
|
||||||
|
playbook_ua = parse(playbook_ua_string)
|
||||||
|
nexus_7_ua = parse(nexus_7_ua_string)
|
||||||
|
windows_phone_ua = parse(windows_phone_ua_string)
|
||||||
|
windows_rt_ua = parse(windows_rt_ua_string)
|
||||||
|
blackberry_torch_ua = parse(blackberry_torch_ua_string)
|
||||||
|
blackberry_bold_ua = parse(blackberry_bold_ua_string)
|
||||||
|
blackberry_bold_touch_ua = parse(blackberry_bold_touch_ua_string)
|
||||||
|
j2me_opera_ua = parse(j2me_opera_ua_string)
|
||||||
|
ie_ua = parse(ie_ua_string)
|
||||||
|
ie_touch_ua = parse(ie_touch_ua_string)
|
||||||
|
mac_safari_ua = parse(mac_safari_ua_string)
|
||||||
|
windows_ie_ua = parse(windows_ie_ua_string)
|
||||||
|
ubuntu_firefox_ua = parse(ubuntu_firefox_ua_string)
|
||||||
|
google_bot_ua = parse(google_bot_ua_string)
|
||||||
|
nokia_n97_ua = parse(nokia_n97_ua_string)
|
||||||
|
android_firefox_aurora_ua = parse(android_firefox_aurora_ua_string)
|
||||||
|
thunderbird_ua = parse(thunderbird_ua_string)
|
||||||
|
outlook_ua = parse(outlook_usa_string)
|
||||||
|
chromebook_ua = parse(chromebook_ua_string)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAgentsTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_user_agent_object_assignments(self):
|
||||||
|
ua_dict = user_agent_parser.Parse(devices['iphone']['ua_string'])
|
||||||
|
iphone_ua = devices['iphone']['user_agent']
|
||||||
|
|
||||||
|
# Ensure browser attributes are assigned correctly
|
||||||
|
self.assertEqual(iphone_ua.browser.family,
|
||||||
|
ua_dict['user_agent']['family'])
|
||||||
|
self.assertEqual(
|
||||||
|
iphone_ua.browser.version,
|
||||||
|
(int(ua_dict['user_agent']['major']),
|
||||||
|
int(ua_dict['user_agent']['minor']))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure os attributes are assigned correctly
|
||||||
|
self.assertEqual(iphone_ua.os.family, ua_dict['os']['family'])
|
||||||
|
self.assertEqual(
|
||||||
|
iphone_ua.os.version,
|
||||||
|
(int(ua_dict['os']['major']), int(ua_dict['os']['minor']))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ensure device attributes are assigned correctly
|
||||||
|
self.assertEqual(iphone_ua.device.family,
|
||||||
|
ua_dict['device']['family'])
|
||||||
|
|
||||||
|
def test_is_tablet_property(self):
|
||||||
|
self.assertFalse(iphone_ua.is_tablet)
|
||||||
|
self.assertFalse(galaxy_s3_ua.is_tablet)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_tablet)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_tablet)
|
||||||
|
self.assertFalse(windows_phone_ua.is_tablet)
|
||||||
|
self.assertFalse(ie_ua.is_tablet)
|
||||||
|
self.assertFalse(ie_touch_ua.is_tablet)
|
||||||
|
self.assertFalse(mac_safari_ua.is_tablet)
|
||||||
|
self.assertFalse(windows_ie_ua.is_tablet)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_tablet)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_tablet)
|
||||||
|
self.assertFalse(google_bot_ua.is_tablet)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_tablet)
|
||||||
|
self.assertTrue(windows_rt_ua.is_tablet)
|
||||||
|
self.assertTrue(ipad_ua.is_tablet)
|
||||||
|
self.assertTrue(playbook_ua.is_tablet)
|
||||||
|
self.assertTrue(kindle_fire_ua.is_tablet)
|
||||||
|
self.assertTrue(nexus_7_ua.is_tablet)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_tablet)
|
||||||
|
|
||||||
|
def test_is_mobile_property(self):
|
||||||
|
self.assertTrue(iphone_ua.is_mobile)
|
||||||
|
self.assertTrue(galaxy_s3_ua.is_mobile)
|
||||||
|
self.assertTrue(blackberry_torch_ua.is_mobile)
|
||||||
|
self.assertTrue(blackberry_bold_ua.is_mobile)
|
||||||
|
self.assertTrue(windows_phone_ua.is_mobile)
|
||||||
|
self.assertTrue(j2me_opera_ua.is_mobile)
|
||||||
|
self.assertTrue(nokia_n97_ua.is_mobile)
|
||||||
|
self.assertFalse(windows_rt_ua.is_mobile)
|
||||||
|
self.assertFalse(ipad_ua.is_mobile)
|
||||||
|
self.assertFalse(playbook_ua.is_mobile)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_mobile)
|
||||||
|
self.assertFalse(nexus_7_ua.is_mobile)
|
||||||
|
self.assertFalse(ie_ua.is_mobile)
|
||||||
|
self.assertFalse(ie_touch_ua.is_mobile)
|
||||||
|
self.assertFalse(mac_safari_ua.is_mobile)
|
||||||
|
self.assertFalse(windows_ie_ua.is_mobile)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_mobile)
|
||||||
|
self.assertFalse(google_bot_ua.is_mobile)
|
||||||
|
self.assertTrue(android_firefox_aurora_ua.is_mobile)
|
||||||
|
|
||||||
|
def test_is_touch_property(self):
|
||||||
|
self.assertTrue(iphone_ua.is_touch_capable)
|
||||||
|
self.assertTrue(galaxy_s3_ua.is_touch_capable)
|
||||||
|
self.assertTrue(ipad_ua.is_touch_capable)
|
||||||
|
self.assertTrue(playbook_ua.is_touch_capable)
|
||||||
|
self.assertTrue(kindle_fire_ua.is_touch_capable)
|
||||||
|
self.assertTrue(nexus_7_ua.is_touch_capable)
|
||||||
|
self.assertTrue(windows_phone_ua.is_touch_capable)
|
||||||
|
self.assertTrue(ie_touch_ua.is_touch_capable)
|
||||||
|
self.assertTrue(blackberry_bold_touch_ua.is_mobile)
|
||||||
|
self.assertTrue(blackberry_torch_ua.is_mobile)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_touch_capable)
|
||||||
|
self.assertFalse(ie_ua.is_touch_capable)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_touch_capable)
|
||||||
|
self.assertFalse(mac_safari_ua.is_touch_capable)
|
||||||
|
self.assertFalse(windows_ie_ua.is_touch_capable)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_touch_capable)
|
||||||
|
self.assertFalse(google_bot_ua.is_touch_capable)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_touch_capable)
|
||||||
|
self.assertTrue(android_firefox_aurora_ua.is_touch_capable)
|
||||||
|
|
||||||
|
def test_is_pc(self):
|
||||||
|
self.assertFalse(iphone_ua.is_pc)
|
||||||
|
self.assertFalse(galaxy_s3_ua.is_pc)
|
||||||
|
self.assertFalse(ipad_ua.is_pc)
|
||||||
|
self.assertFalse(playbook_ua.is_pc)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_pc)
|
||||||
|
self.assertFalse(nexus_7_ua.is_pc)
|
||||||
|
self.assertFalse(windows_phone_ua.is_pc)
|
||||||
|
self.assertFalse(blackberry_bold_touch_ua.is_pc)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_pc)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_pc)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_pc)
|
||||||
|
self.assertFalse(google_bot_ua.is_pc)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_pc)
|
||||||
|
self.assertTrue(mac_safari_ua.is_pc)
|
||||||
|
self.assertTrue(windows_ie_ua.is_pc)
|
||||||
|
self.assertTrue(ubuntu_firefox_ua.is_pc)
|
||||||
|
self.assertTrue(ie_touch_ua.is_pc)
|
||||||
|
self.assertTrue(ie_ua.is_pc)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_pc)
|
||||||
|
self.assertTrue(chromebook_ua.is_pc)
|
||||||
|
|
||||||
|
def test_is_bot(self):
|
||||||
|
self.assertTrue(google_bot_ua.is_bot)
|
||||||
|
self.assertFalse(iphone_ua.is_bot)
|
||||||
|
self.assertFalse(galaxy_s3_ua.is_bot)
|
||||||
|
self.assertFalse(ipad_ua.is_bot)
|
||||||
|
self.assertFalse(playbook_ua.is_bot)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_bot)
|
||||||
|
self.assertFalse(nexus_7_ua.is_bot)
|
||||||
|
self.assertFalse(windows_phone_ua.is_bot)
|
||||||
|
self.assertFalse(blackberry_bold_touch_ua.is_bot)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_bot)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_bot)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_bot)
|
||||||
|
self.assertFalse(mac_safari_ua.is_bot)
|
||||||
|
self.assertFalse(windows_ie_ua.is_bot)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_bot)
|
||||||
|
self.assertFalse(ie_touch_ua.is_bot)
|
||||||
|
self.assertFalse(ie_ua.is_bot)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_bot)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_bot)
|
||||||
|
|
||||||
|
def test_is_email_client(self):
|
||||||
|
self.assertTrue(thunderbird_ua.is_email_client)
|
||||||
|
self.assertTrue(outlook_ua.is_email_client)
|
||||||
|
self.assertFalse(playbook_ua.is_email_client)
|
||||||
|
self.assertFalse(kindle_fire_ua.is_email_client)
|
||||||
|
self.assertFalse(nexus_7_ua.is_email_client)
|
||||||
|
self.assertFalse(windows_phone_ua.is_email_client)
|
||||||
|
self.assertFalse(blackberry_bold_touch_ua.is_email_client)
|
||||||
|
self.assertFalse(blackberry_torch_ua.is_email_client)
|
||||||
|
self.assertFalse(blackberry_bold_ua.is_email_client)
|
||||||
|
self.assertFalse(j2me_opera_ua.is_email_client)
|
||||||
|
self.assertFalse(mac_safari_ua.is_email_client)
|
||||||
|
self.assertFalse(windows_ie_ua.is_email_client)
|
||||||
|
self.assertFalse(ubuntu_firefox_ua.is_email_client)
|
||||||
|
self.assertFalse(ie_touch_ua.is_email_client)
|
||||||
|
self.assertFalse(ie_ua.is_email_client)
|
||||||
|
self.assertFalse(nokia_n97_ua.is_email_client)
|
||||||
|
self.assertFalse(android_firefox_aurora_ua.is_email_client)
|
||||||
|
|
||||||
|
|
||||||
|
def test_strings(self):
|
||||||
|
self.assertEqual(str(iphone_ua), "iPhone / iOS 5.1 / Mobile Safari 5.1")
|
||||||
|
self.assertEqual(str(ipad_ua), "iPad / iOS 3.2 / Mobile Safari 4.0.4")
|
||||||
|
self.assertEqual(str(galaxy_tab), "Samsung SCH-I800 / Android 2.2 / Android 2.2")
|
||||||
|
self.assertEqual(str(galaxy_s3_ua), "Samsung GT-I9300 / Android 4.0.4 / Android 4.0.4")
|
||||||
|
self.assertEqual(str(kindle_fire_ua), "Kindle / Android / Amazon Silk 1.1.0-80")
|
||||||
|
self.assertEqual(str(playbook_ua), "BlackBerry Playbook / BlackBerry Tablet OS 2.0.1 / BlackBerry WebKit 2.0.1")
|
||||||
|
self.assertEqual(str(nexus_7_ua), "Asus Nexus 7 / Android 4.1.1 / Chrome 18.0.1025")
|
||||||
|
self.assertEqual(str(windows_phone_ua), "Samsung SGH-i917 / Windows Phone 7.5 / IE Mobile 9.0")
|
||||||
|
self.assertEqual(str(windows_rt_ua), "PC / Windows RT / IE 10.0")
|
||||||
|
self.assertEqual(str(blackberry_torch_ua), "BlackBerry 9800 / BlackBerry OS 6.0.0 / BlackBerry WebKit 6.0.0")
|
||||||
|
self.assertEqual(str(blackberry_bold_ua), "BlackBerry 9700 / BlackBerry OS 5.0.0 / BlackBerry 9700")
|
||||||
|
self.assertEqual(str(blackberry_bold_touch_ua), "BlackBerry 9930 / BlackBerry OS 7.0.0 / BlackBerry WebKit 7.0.0")
|
||||||
|
self.assertEqual(str(j2me_opera_ua), "Generic Feature Phone / Other / Opera Mini 9.80")
|
||||||
|
self.assertEqual(str(ie_ua), "PC / Windows 8 / IE 10.0")
|
||||||
|
self.assertEqual(str(ie_touch_ua), "PC / Windows 8 / IE 10.0")
|
||||||
|
self.assertEqual(str(mac_safari_ua), "PC / Mac OS X 10.6.8 / WebKit Nightly 537.13")
|
||||||
|
self.assertEqual(str(windows_ie_ua), "PC / Windows 7 / IE 9.0")
|
||||||
|
self.assertEqual(str(ubuntu_firefox_ua), "PC / Ubuntu / Firefox 15.0.1")
|
||||||
|
self.assertEqual(str(google_bot_ua), "Spider / Other / Googlebot 2.1")
|
||||||
|
self.assertEqual(str(nokia_n97_ua), "Nokia N97 / Symbian OS 9.4 / Nokia Browser 7.1.12344")
|
||||||
|
self.assertEqual(str(android_firefox_aurora_ua), "Generic Smartphone / Android / Firefox Mobile 27.0")
|
||||||
|
|
||||||
|
def test_unicode_strings(self):
|
||||||
|
try:
|
||||||
|
# Python 2
|
||||||
|
unicode_ua_str = unicode(devices['iphone']['user_agent'])
|
||||||
|
self.assertEqual(unicode_ua_str,
|
||||||
|
u"iPhone / iOS 5.1 / Mobile Safari 5.1")
|
||||||
|
self.assertTrue(isinstance(unicode_ua_str, unicode))
|
||||||
|
except NameError:
|
||||||
|
# Python 3
|
||||||
|
unicode_ua_str = str(devices['iphone']['user_agent'])
|
||||||
|
self.assertEqual(unicode_ua_str,
|
||||||
|
"iPhone / iOS 5.1 / Mobile Safari 5.1")
|
||||||
|
|
||||||
|
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), 'devices.json')) as f:
|
||||||
|
devices = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrapper(items):
|
||||||
|
def test_func(self):
|
||||||
|
attrs = ('is_bot', 'is_mobile',
|
||||||
|
'is_pc', 'is_tablet', 'is_touch_capable')
|
||||||
|
for attr in attrs:
|
||||||
|
self.assertEqual(
|
||||||
|
getattr(items['user_agent'], attr), items[attr], msg=attr)
|
||||||
|
# Temporarily commenting this out since UserAgent.device
|
||||||
|
# may return different string depending ua-parser version
|
||||||
|
# self.assertEqual(str(items['user_agent']), items['str'])
|
||||||
|
return test_func
|
||||||
|
|
||||||
|
for device, items in compat.iteritems(devices):
|
||||||
|
items['user_agent'] = parse(items['ua_string'])
|
||||||
|
setattr(UserAgentsTest, 'test_' + device, test_wrapper(items))
|
||||||
30
app_common/models/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Sunpop.cn. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
# Created on 2019-04-20
|
||||||
|
# author: 广州尚鹏,http://www.sunpop.cn
|
||||||
|
# email: 300883@qq.com
|
||||||
|
# resource of Sunpop
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
# Odoo12在线用户手册(长期更新)
|
||||||
|
# http://www.sunpop.cn/documentation/user/12.0/en/index.html
|
||||||
|
|
||||||
|
# Odoo12在线开发者手册(长期更新)
|
||||||
|
# http://www.sunpop.cn/documentation/12.0/index.html
|
||||||
|
|
||||||
|
# Odoo10在线中文用户手册(长期更新)
|
||||||
|
# http://www.sunpop.cn/documentation/user/10.0/zh_CN/index.html
|
||||||
|
|
||||||
|
# Odoo10离线中文用户手册下载
|
||||||
|
# http://www.sunpop.cn/odoo10_user_manual_document_offline/
|
||||||
|
# Odoo10离线开发手册下载-含python教程,jquery参考,Jinja2模板,PostgresSQL参考(odoo开发必备)
|
||||||
|
# http://www.sunpop.cn/odoo10_developer_document_offline/
|
||||||
|
# description:
|
||||||
|
|
||||||
|
from . import base
|
||||||
|
from . import ir_ui_view
|
||||||
|
from . import ir_cron
|
||||||
|
from . import res_users
|
||||||
|
|
||||||
|
|
||||||
108
app_common/models/base.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
|
||||||
|
|
||||||
|
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:
|
||||||
|
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):
|
||||||
|
if not url:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
response = requests.get(url) # 将这个图片保存在内存
|
||||||
|
except Exception as e:
|
||||||
|
return None
|
||||||
|
# 返回这个图片的base64编码
|
||||||
|
return base64.b64encode(BytesIO(response.content).read())
|
||||||
15
app_common/models/ir_cron.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
import logging
|
||||||
|
from odoo import api, fields, models, modules, tools, _
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class IrCron(models.Model):
|
||||||
|
_inherit = "ir.cron"
|
||||||
|
|
||||||
|
trigger_user_id = fields.Many2one('res.users', string='Last Trigger User')
|
||||||
|
|
||||||
|
def method_direct_trigger(self):
|
||||||
|
self.write({'trigger_user_id': self.env.user.id})
|
||||||
|
return super(IrCron, self).method_direct_trigger()
|
||||||
41
app_common/models/ir_ui_view.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# -*- 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, *args, **kwargs):
|
||||||
|
super(View, self).__init__(*args, **kwargs)
|
||||||
|
view_validation.relaxng = app_relaxng
|
||||||
|
|
||||||
|
# todo: 有可能需要处理增加的 header等标签
|
||||||
|
# 直接重写原生方法
|
||||||
|
# def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False):
|
||||||
10
app_common/models/res_users.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from odoo import api, fields, models, tools, _
|
||||||
|
|
||||||
|
|
||||||
|
class ResUsers(models.Model):
|
||||||
|
_inherit = 'res.users'
|
||||||
|
|
||||||
|
login = fields.Char(index=True)
|
||||||
2
app_common/report/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
433
app_common/rng/common.rng
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
<?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="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:attribute name="context"/>
|
||||||
|
</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:choice>
|
||||||
|
</rng:oneOrMore>
|
||||||
|
</rng:element>
|
||||||
|
</rng:define>
|
||||||
|
|
||||||
|
</rng:grammar>
|
||||||
44
app_common/rng/search_view.rng
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?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: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>
|
||||||
105
app_common/rng/tree_view.rng
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<?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="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>
|
||||||
51
app_common/security/app_security.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
<!-- model权限 -->
|
||||||
|
<record id="acc_module" model="ir.model.access">
|
||||||
|
<field name="name">acc_module_user</field>
|
||||||
|
<field name="model_id" ref="model_app_order"/>
|
||||||
|
<field name="group_id" ref="base.group_user"/>
|
||||||
|
<field name="perm_read" eval="1"/>
|
||||||
|
<field name="perm_write" eval="1"/>
|
||||||
|
<field name="perm_create" eval="1"/>
|
||||||
|
<field name="perm_unlink" eval="1"/>
|
||||||
|
</record>
|
||||||
|
<!-- 应用权限目录 -->
|
||||||
|
<record model="ir.module.category" id="app_module_category_1">
|
||||||
|
<field name="name">App...</field>
|
||||||
|
<field name="description">Helps you manage your ...</field>
|
||||||
|
<field name="sequence">8</field>
|
||||||
|
</record>
|
||||||
|
<!-- 权限用户 -->
|
||||||
|
<!-- 普通 -->
|
||||||
|
<record id="group_app_user" model="res.groups">
|
||||||
|
<field name="name">App User</field>
|
||||||
|
<field name="category_id" ref="app_module_category_1"/>
|
||||||
|
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
|
||||||
|
<field name="comment">The user will be able to ...</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- 管理员,可以配置 -->
|
||||||
|
<record id="group_app_admin" model="res.groups">
|
||||||
|
<field name="name">App Admin</field>
|
||||||
|
<field name="category_id" ref="app_module_category_1"/>
|
||||||
|
<field name="implied_ids" eval="[(4, ref('group_app_user'))]"/>
|
||||||
|
<field name="comment">The user will be able to config ...</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- group_erp_manager自动有完整权限 -->
|
||||||
|
<record id="base.group_erp_manager" model="res.groups">
|
||||||
|
<field name="implied_ids" eval="[(4, ref('group_app_admin'))]"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Rules -->
|
||||||
|
<record id="rule_user" model="ir.rule">
|
||||||
|
<field name="name">Users are allowed to access their own m///</field>
|
||||||
|
<field name="model_id" ref="model_app_order"/>
|
||||||
|
<field name="domain_force">['|', ('partner_id', 'in', [user.partner_id.id]), ('user_id.id', '=', user.id)]</field>
|
||||||
|
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
|
||||||
|
</record>
|
||||||
|
<!--End -->
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
3
app_common/security/ir.model.access.csv
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_product_attribute_value_group_user,product_attribute_value_group.user,app_product_variant_pro.model_product_attribute_value_group,base.group_user,1,0,0,0
|
||||||
|
access_product_attribute_value_group_manager,product_attribute_value_group.manager,app_product_variant_pro.model_product_attribute_value_group,base.group_erp_manager,1,1,1,1
|
||||||
|
BIN
app_common/static/description/banner.png
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
app_common/static/description/icon.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
103
app_common/static/description/index.html
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced" >
|
||||||
|
<div class="oe_span12">
|
||||||
|
<h2 class="oe_slogan"> </h2>
|
||||||
|
<h3 class="oe_slogan"> </h3>
|
||||||
|
<div class="oe_row">
|
||||||
|
<h3>Lastest update: v15.21.11.30</h3>
|
||||||
|
<div class="oe_span12">
|
||||||
|
<img class="oe_demo oe_screenshot" 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 15, 14, 13, 12, 11, Enterprise and Community Edition.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h1 class="text-danger text-center">Setup, please run the follow command to install the lib.</h1>
|
||||||
|
<h4 class="oe_slogan"> pip install pyyaml ua-parser user-agents </h4>
|
||||||
|
<div class="oe_demo oe_screenshot">
|
||||||
|
<img src=".jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h1 class="text-danger text-center">So Easy to navigator and search any data.</h1>
|
||||||
|
<h4 class="oe_slogan"> </h4>
|
||||||
|
<div class="oe_demo oe_screenshot">
|
||||||
|
<img src=".jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container">
|
||||||
|
<div class="oe_row oe_spaced">
|
||||||
|
<h1 class="text-danger text-center">Multi-language Support..</h1>
|
||||||
|
<h4 class="oe_slogan"> </h4>
|
||||||
|
<div class="oe_demo oe_screenshot">
|
||||||
|
<img src="cnreadme.jpg"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="oe_container oe_dark">
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<div class="oe_span12">
|
||||||
|
<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.sunpop.cn</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="oe_row oe_spaced text-center">
|
||||||
|
<h1>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=Sunpop.cn">Supop.cn Odoo Addons</a>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
BIN
app_common/static/img/icon_sunpop.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
app_common/static/img/logo_sunpop.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
0
app_common/static/src/js/sunpop.cn
Normal file
0
app_common/static/src/xml/sunpop.cn
Normal file
13
app_common/views/ir_cron_views.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="app_ir_cron_view_tree" model="ir.ui.view">
|
||||||
|
<field name="name">app.ir.cron.tree</field>
|
||||||
|
<field name="model">ir.cron</field>
|
||||||
|
<field name="inherit_id" ref="base.ir_cron_view_tree"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='active']" position="before">
|
||||||
|
<field name='trigger_user_id' optional="show"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
116
app_common/views/product_brand_views.xml
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="app_product_brand_search_view" model="ir.ui.view">
|
||||||
|
<field name="name">product.brand.search</field>
|
||||||
|
<field name="model">product.brand</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Product Brand">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<act_window
|
||||||
|
id="action_open_brand_products"
|
||||||
|
name="Brand Products"
|
||||||
|
res_model="product.template"
|
||||||
|
view_mode="kanban,form,tree"
|
||||||
|
domain="[('product_brand_id', '=', active_id)]"/>
|
||||||
|
|
||||||
|
<act_window
|
||||||
|
id="action_open_single_product_brand"
|
||||||
|
name="Product Brand"
|
||||||
|
res_model="product.brand"
|
||||||
|
view_mode="kanban,form,tree"
|
||||||
|
target="current"
|
||||||
|
domain="[('product_ids', 'in', active_id)]"/>
|
||||||
|
|
||||||
|
<record id="app_product_brand_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">product.brand.form</field>
|
||||||
|
<field name="model">product.brand</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<div class="oe_button_box" name="button_box">
|
||||||
|
<button name="%(action_open_brand_products)d"
|
||||||
|
type="action"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-cubes">
|
||||||
|
<field name="products_count" widget="statinfo" string="Products"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<field name="logo" widget="image" class="oe_avatar"/>
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="name" string="Brand Name" class="oe_edit_only"/>
|
||||||
|
<h1><field name="name"/></h1>
|
||||||
|
</div>
|
||||||
|
<group>
|
||||||
|
<group>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group string="Description">
|
||||||
|
<field name="description" nolabel="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_brand_tree_view" model="ir.ui.view">
|
||||||
|
<field name="name">product.brand.tree</field>
|
||||||
|
<field name="model">product.brand</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="product.brand">
|
||||||
|
<field name="sequence" widget="handle"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="description"/>
|
||||||
|
<field name="partner_id"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_brand_kanban_view" model="ir.ui.view">
|
||||||
|
<field name="name">product.brand.kanban</field>
|
||||||
|
<field name="model">product.brand</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<kanban>
|
||||||
|
<field name="id"/>
|
||||||
|
<field name="logo"/>
|
||||||
|
<field name="products_count"/>
|
||||||
|
<field name="description"/>
|
||||||
|
<templates>
|
||||||
|
<t t-name="kanban-box">
|
||||||
|
<div class="oe_kanban_global_click">
|
||||||
|
<div class="o_kanban_image">
|
||||||
|
<img t-att-src="kanban_image('product.brand', 'logo', record.id.raw_value)" alt="Logo"/>
|
||||||
|
</div>
|
||||||
|
<div class="oe_kanban_details">
|
||||||
|
<h4>
|
||||||
|
<field name="name"/>
|
||||||
|
</h4>
|
||||||
|
<div>
|
||||||
|
<a name="%(action_open_brand_products)d" type="action">
|
||||||
|
<t t-esc="record.products_count.value"/> Products
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
|
</kanban>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record model="ir.actions.act_window" id="action_product_brand">
|
||||||
|
<field name="name">Brand</field>
|
||||||
|
<field name="res_model">product.brand</field>
|
||||||
|
<field name="view_mode">kanban,form,tree</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_product_brand" name="Product Brands"
|
||||||
|
action="action_product_brand"
|
||||||
|
sequence="8"
|
||||||
|
parent="stock.menu_product_in_config_stock"/>
|
||||||
|
</odoo>
|
||||||
24
app_common/views/product_product_views.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!--产品list,可调整显示顺序-->
|
||||||
|
<record id="app_product_product_tree_view" model="ir.ui.view">
|
||||||
|
<field name="name">app.product.product.tree</field>
|
||||||
|
<field name="model">product.product</field>
|
||||||
|
<field name="inherit_id" ref="product.product_product_tree_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='type']" position="after">
|
||||||
|
<field name="abc_classification" widget="label_selection"
|
||||||
|
options="{'classes': {'a': 'danger', 'b': 'primary', 'c': 'default'}}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_product_action" model="ir.actions.act_window">
|
||||||
|
<field name="name">Product Manager</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">product.product</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
59
app_common/views/product_template_views.xml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="view_product_template_search_brand" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.search.brand</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_search_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="product_brand_id"/>
|
||||||
|
<filter string="Brand" name="groupby_brand" domain="[]"
|
||||||
|
context="{'group_by': 'product_brand_id'}"/>
|
||||||
|
<separator/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_template_form_brand_add" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.product.form</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="sale_ok" position="before">
|
||||||
|
<field name="product_brand_id" placeholder="Brand"/>
|
||||||
|
<div/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_product_template_kanban_brand" model="ir.ui.view">
|
||||||
|
<field name="name">product kanban view add brand</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_kanban_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[hasclass('oe_kanban_details')]/strong[1]" position="after">
|
||||||
|
<div>
|
||||||
|
<a t-if="record.product_brand_id" type="action"
|
||||||
|
name="%(action_open_single_product_brand)d">
|
||||||
|
<field name="product_brand_id"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_product_template_tree_brand" model="ir.ui.view">
|
||||||
|
<field name="name">product tree view add brand</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_tree_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="name" position="after">
|
||||||
|
<field name="product_brand_id"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||