跳转至内容

Odoo 开发与实施交流

2.3k 主题 10.4k 帖子

No decsciption available

  • 0 赞同
    1 帖子
    981 浏览
    G
    原文更好阅读体验,不断更新。 可访问: https://www.sunpop.cn/odoo13_chinese_translate_transifex_profession/ 参与者(提PR)附送odoo13模块。 ====================================== odoo13 国庆献礼! 新发布的odoo13 在功能、性能、界面都有极大的更新,特别是企业版,在生产供应链和hr都增加了很多激动人心的功能,在可预见的将来必然会蓬勃发展。有关 odoo13 的在功能、性能、界面上各种功能增强,请参阅 我们的odoo13专题。 广州尚鹏是 odoo 官方合作伙伴,亦可审核 Transifex 上的中文化翻译。 为推动 odoo 中文化的发展,同时为保证自有商业客户的正常使用,建立此开源代码仓库repo进行 odoo13 中文化翻译。详情请查阅 https://github.com/guohuadeng/odoo13-cn ================================== 版本 source: 社区版翻译 source_ent: 企业版翻译 翻译规范 我们使用 odoo 对标 SAP 系统,故翻译上主要也参考 SAP。其次参考 MRPII 规范。在ref目录有相关文档。 SAP 术语 MRPII 术语 行业标准术语 如何贡献 odoo13 翻译 odoo13 官方翻译主要通过 transifex.com ,由于 transifex 上的使用方便和审核效率问题,我们同时也建立了这个仓库,力争更快更精确的实现 odoo13 中文化。 方法一:通过我们的 github 贡献翻译 仓库地址:https://github.com/guohuadeng/odoo13-cn 更新提交:使用 git PR(pull request),参考 https://www.jianshu.com/p/d921828bf623 建议:在此 repo 上提 issue 方法二:通过 transifex.com 注册登录 http://www.transifex.com 加入 odoo 中国团队 https://www.transifex.com/odoo/teams/ 参与 odoo13 翻译 总览: https://www.transifex.com/odoo/odoo-13/dashboard/ 模块翻译: https://www.transifex.com/odoo/odoo-13/translate/#zh_CN po下载与上传: https://www.transifex.com/odoo/odoo-13/language/zh_CN/ 文件夹说明 ├─ref 参考文档 ├─source 通过 .tx 获取的odoo的 PO 翻译文档,并更新,可直接用于 odoo13 ├─source_ent 同上,企业版 获取最新 .po 文件,更新后发布至 transifex 安装 tx 客户端,请先自行安装 pip3 pip3 install transifex-client -i https://mirrors.ustc.edu.cn/pypi/web/simple 取最新的 po,先进入 /source 目录 cd source tx pull -l zh_CN 查看下载的 po 文件并更改,在 ./source 目录下,即可看到相关的 odoo 翻译文件 推送翻译文件至 tx,参考 https://docs.transifex.com/transifex-github-integrations/github-tx-client tx push -t
  • 0 赞同
    3 帖子
    1k 浏览
    Q
    是呀模块化迭代多了,对于不熟悉模块的人来说很痛苦。
  • 0 赞同
    6 帖子
    6k 浏览
    G
    @月满西楼 报错多数是有些python库没下载,多数是墙或者python list更新原因,不影响大局。 本地包是指你从官网下载,上传到服务器上。远程的就是直接wget。 本地宝多数用于企业版,这个都有说明,可以看下readme。
  • odoo11代码放到odoo12 安装不显示的问题

    2
    0 赞同
    2 帖子
    1k 浏览
    JoshuaJ
    如果是安装成功,先检查下模块的model是否在odoo里生成?视图挂载的锚点是否在12里面也存在? 当然也有可能是其他问题,那就要通过查看日志去定位了。
  • 请大腿帮忙看一下这个问题:"relation "ir_sequence_003" already exists

    6
    0 赞同
    6 帖子
    2k 浏览
    @digitalsatori 您好,我发生错误的之前操作是安装 中国-城市数据 模块,后面发现只要涉及到系统自带的开票模块(Account)的都会有这样的问题产生,我现在将这个文件夹下的所有序列代码都注释了才装成功()。我也想请教一下,如何去在数据库中查找您所说的序列。。。 <!-- Account Statement Sequences 对账单序列 --> <record id="sequence_reconcile_seq" model="ir.sequence"> <field name="name">Account reconcile sequence</field> <field name="code">account.reconcile</field> <field name="prefix">A</field> <field eval="1" name="number_next"/> <field eval="1" name="number_increment"/> <field eval="False" name="company_id"/> </record> [image: 1570608047419-1570607994555.jpg]
  • 卓忆原创连载:我和Odoo的故事二:选择Odoo的理由

    1
    2
    0 赞同
    1 帖子
    703 浏览
    卓忆
    选择Odoo的理由 当时还叫OpenERP, 一:首先,看着还觉得顺眼吧,红白配色,那个小蚂蚁感觉也很有力量和生机: [image: 1570511680188-0140c565-7bae-4701-831e-f4a44c2579da-image.png] 二:适用于小公司的模块化的设计,需要的装,不需要就不装,可以随着公司成长增加模块。 对于小公司来说,本来也没什么流程,比较随意,有这么个开源的ERP来重塑下流程也挺好。(当年的那些图标看起来和速达什么竟然还有些相似。。) [image: 1570511707534-ce3d261d-45eb-48d7-86c2-b098f784c34e-image.png] 三:重点来了:居然还是全开源的,也就是说:完全看得到是怎么实现的!还有各种社区支持。 想想现在很多餐厅的透明厨房,可能也是受到开源文化的影响吧! 而且当时已经有了相当多的用户积累,在中国还有好几个社区,以网站,QQ等形式存在着,汉化,本地化这些事,就是社区的先行者们在做的,当时就觉得:怎么才看到这个软件,有了社区的帮助,很多坑就能爬出来了,没有社区的话,想想简直是。。。所以至今依然带着一份感激,谢谢大家。 截止到2019年10月,官方数据Odoo已经拥有400万+用户;在中国社区也依然存在着,就像《大教堂和集市》中提到的,各种社区就像各种集市一样,提供着各种服务。
  • odoo开箱即用: 体现真实生产成本,才能体现真实订单利润

    1
    18
    0 赞同
    1 帖子
    719 浏览
    2264082
    odoo开箱即用: 体现真实生产成本,才能体现真实订单利润 场景 : 企业依据客户订单生产定制一制成品(制程经历委外加工),生产过程中除了原料成本,也发生了其他生产成本支出(如内部加工支出和委外加工费支出),因此加入这些相关生产成本,来忠实体现委外制成品的真实成本 [image: 1569679690529-2019-09-27_130606.png] [image: 1569679706291-2019-09-27_130757.png] [image: 1569679715457-2019-09-27_130831.png] [image: 1569679723158-2019-09-27_130908.png] [image: 1569679732100-2019-09-27_130943.png] [image: 1569679741049-2019-09-27_131020.png] [image: 1569679748405-2019-09-27_131112.png] [image: 1569679760344-2019-09-27_131315.png] [image: 1569679768440-2019-09-27_131342.png] [image: 1569679781291-2019-09-27_132503.png] [image: 1569679790208-2019-09-27_132534.png] [image: 1569679802037-2019-09-27_132642.png] [image: 1569679810827-2019-09-27_133107.png] [image: 1569679874709-2019-09-27_133429.png] [image: 1569679882803-2019-09-27_133500.png] [image: 1569679891895-2019-09-27_133549.png] [image: 1569679900789-2019-09-27_133629.png] [image: 1569679911059-2019-09-27_133739.png] 台北-226408 QQ = 3045661196 Mail= [email protected] odoo 实施"开箱即用"
  • 网站的联系我们可以发送邮件吗

    2
    1
    0 赞同
    2 帖子
    876 浏览
    D
    正确设置的情况下是直接关联后台商机的。
  • 关于odoo查询

    1
    1
    0 赞同
    1 帖子
    719 浏览
    L
    现在网站产品详情切换产品颜色属性时,想要改变产品名称。 产品名称是自己添加的字段, 即一个变体对应一个名称。 [image: 1569375450757-b53ec398-b330-4bee-8e1d-c04b7a5469ed-image.png] 我在后台找到了这段查询的代码 if request.env.ref('website_sale.shop_product_carousel', raise_if_not_found=False): # IF for compatibility 12.0 res.update(carousel=request.env['ir.ui.view'].render_template('website_sale.shop_product_carousel', values={ 'product': request.env['product.template'].browse(res['product_template_id']), 'product_variant': request.env['product.product'].browse(res['product_id']), })) return res 想要让返回的product_variant对象中加上我自己添加的产品名称字段, 请问该怎么实现? 在线等各位铁汁指教。
  • 0 赞同
    1 帖子
    579 浏览
    L
    在库存设置中保存设置时右上角弹窗 下列字段无效:Google客户端密钥, 请问问题出在哪该如何解决
  • 产品列表新增搜索条件

    2
    1
    0 赞同
    2 帖子
    1k 浏览
    JoshuaJ
    你可以开启debug模式,看看这个搜索视图是否已经被你成功修改。
  • 卸载模块应用问题?

    1
    1
    0 赞同
    1 帖子
    688 浏览
    X
    为什么卸载模块后,原来模块中的记录无法被删除掉,有的模块又可以,有的又不行,如图。[image: 1567473185529-qq%E6%8B%BC%E9%9F%B3%E6%88%AA%E5%9B%BE20190903091249.jpg]
  • 想写一个接口供异步调用,但添加后发现访问不到

    2
    0 赞同
    2 帖子
    924 浏览
    D
    你应该是有多个数据库,在服务器启动时用 -d 数据库名 指定一下数据库,再试
  • 为何我的Database manager使用不了

    2
    1
    0 赞同
    2 帖子
    763 浏览
    D
    看一下Odoo的log才能知道。如果有niginx做反向代理,看一下upload file size的设置,以及超时设置的值。
  • controllers中的auth=“user”对应的方法,如何使用?

    1
    0 赞同
    1 帖子
    698 浏览
    E
    例如: from odoo import http class Books(http.Controller): @http.route('/library/books', auth='user') def list(self, **kwargs): Book = http.request.env['library.book'] books = Book.search([]) return http.request.render( 'library_app.book_list_template', {'books':books}) 如果auth='public',那么,可以在postman中,通过:http://localhost:8069/library/books来访问这个方法; 但是,auth='user'时,得到的结果是登陆页面的代码。 那么,如何操作,才能像auth='public'那样访问这个方法?
  • odoo12安装制造模块提示odoo sever error

    1
    0 赞同
    1 帖子
    447 浏览
    S
    错误: Odoo Server Error Traceback (most recent call last): File "F:\Odoo\odoo12\odoo\http.py", line 656, in _handle_exception return super(JsonRequest, self)._handle_exception(exception) File "F:\Odoo\odoo12\odoo\http.py", line 314, in _handle_exception raise pycompat.reraise(type(exception), exception, sys.exc_info()[2]) File "F:\Odoo\odoo12\odoo\tools\pycompat.py", line 87, in reraise raise value File "F:\Odoo\odoo12\odoo\http.py", line 698, in dispatch result = self._call_function(**self.params) File "F:\Odoo\odoo12\odoo\http.py", line 346, in _call_function return checked_call(self.db, *args, **kwargs) File "F:\Odoo\odoo12\odoo\service\model.py", line 97, in wrapper return f(dbname, *args, **kwargs) File "F:\Odoo\odoo12\odoo\http.py", line 339, in checked_call result = self.endpoint(*a, **kw) File "F:\Odoo\odoo12\odoo\http.py", line 941, in call return self.method(*args, **kw) File "F:\Odoo\odoo12\odoo\http.py", line 519, in response_wrap response = f(*args, **kw) File "f:\odoo\odoo12\addons\web\controllers\main.py", line 966, in call_button action = self._call_kw(model, method, args, {}) File "f:\odoo\odoo12\addons\web\controllers\main.py", line 954, in _call_kw return call_kw(request.env[model], method, args, kwargs) File "F:\Odoo\odoo12\odoo\api.py", line 749, in call_kw return _call_kw_multi(method, model, args, kwargs) File "F:\Odoo\odoo12\odoo\api.py", line 736, in _call_kw_multi result = method(recs, *args, **kwargs) File "", line 2, in button_immediate_install File "f:\odoo\odoo12\odoo\addons\base\models\ir_module.py", line 74, in check_and_log return method(self, *args, **kwargs) File "f:\odoo\odoo12\odoo\addons\base\models\ir_module.py", line 445, in button_immediate_install return self._button_immediate_function(type(self).button_install) File "f:\odoo\odoo12\odoo\addons\base\models\ir_module.py", line 561, in _button_immediate_function modules.registry.Registry.new(self._cr.dbname, update_module=True) File "F:\Odoo\odoo12\odoo\modules\registry.py", line 86, in new odoo.modules.load_modules(registry._db, force_demo, status, update_module) File "F:\Odoo\odoo12\odoo\modules\loading.py", line 421, in load_modules loaded_modules, update_module, models_to_check) File "F:\Odoo\odoo12\odoo\modules\loading.py", line 313, in load_marked_modules perform_checks=perform_checks, models_to_check=models_to_check File "F:\Odoo\odoo12\odoo\modules\loading.py", line 222, in load_module_graph load_data(cr, idref, mode, kind='data', package=package, report=report) File "F:\Odoo\odoo12\odoo\modules\loading.py", line 68, in load_data tools.convert_file(cr, package.name, filename, idref, mode, noupdate, kind, report) File "F:\Odoo\odoo12\odoo\tools\convert.py", line 807, in convert_file convert_xml_import(cr, module, fp, idref, mode, noupdate, report) File "F:\Odoo\odoo12\odoo\tools\convert.py", line 869, in convert_xml_import obj = xml_import(cr, module, idref, mode, report=report, noupdate=noupdate, xml_filename=xml_filename) TypeError: new() got an unexpected keyword argument 'report'
  • 模型权限的设置问题

    2
    1
    0 赞同
    2 帖子
    1k 浏览
    X
    也许可以尝试从字段权限、菜单权限方面去思考?
  • 请教如何修改odoo 表单的格式

    1
    1
    0 赞同
    1 帖子
    795 浏览
    X
    [image: 1565948550290-8cdc6008-0ce4-465f-a6b7-2da5179f180f-image.png] 像这样,行列别压缩得惨不忍睹,请问有什么办法可以设置的好看一些呢
  • odoo开发规范,囊括模块视图后端前端规范,以odoo12为基准,

    1
    0 赞同
    1 帖子
    2k 浏览
    G
    作为全球第一的开源ERP,开源的作用就是让您根据本企业业务特性,灵活的定制,实现动态的IT系统,可随着业务变化优化您的ERP,进而优化管理运营。 对中大型企业而言,odoo必须进行开发才可落地,所以一个良好的开发规范,将更有助于团队协助,敏捷开发上线。 本文内容亦是我们进行odoo开发的内部规范,主要参考官方开发教程翻译,原文在此: 英文: https://www.sunpop.cn/documentation/12.0/reference/guidelines.html 中文: https://www.sunpop.cn/odoo_develop_guidelines_python_widget_javascript/ odoo开发规范 本文介绍 Odoo 编码指南。其目的是提高 Odoo 应用程序代码的质量。事实上,正确的代码提高了可读性、简化维护、有助于调试、降低复杂性并提高可靠性。这些准则应该应用于每个新模块的开发和所有新开发。 警告 在stable(稳定)版本中修改现有文件时,原始文件样式将严格取代任何其他样式准则。换句话说,请不要修改odoo正式发布的已有文件或代码,以应用这些准则。它避免了中断代码行的修订历史记录。差异应保持在最小。有关详细信息,请参阅odoo官方的 pull request guide指南。 警告 修改master(主开发)版本中的现有文件时,仅可针对 revision(正在修订时)版本中的代码或大多数文件,将这些准则应用于现有代码。换句话说,仅当现有文件结构发生重大更改时,才修改它。在这种情况下,首先执行 move commit,然后才应用与其相关的更改。 模块结构 目录及对应作用 data/:演示和数据文件 models/:模型定义 controllers/:控制器(包含HTTP路由) views/:视图和模版 static/:web资源,包括css/, js/, img/, lib/, ... wizard/:向导及其视图 report/:报表 tests/:单元测试代码 文件命名 业务model放置在一个文件里,如果模块只包含一个model,它的名字就与模块名一致。如: models/<main_model>.py models/<inherited_main_model>.py views/<main_model>_templates.xml views/<main_model>_views.xml data/<main_model>_demo.xml data/<main_model>_data.xml 例如:销售模块包含sale_order和sale_order_line两个模型,并且sale_order是主模型,所以文件将被命名为models/sale_order.py和 views/sale_order_views.py。 对于数据文件命名,按用途进行命名:demo或者data。例如:data/sale_order_demo.xml和data/sale_order_data.xml 每个模块的控制器都放在一个文件中,命名为main.py。如果是从另一个模块继承的,则将其命名为<module_name>.py。 与模型不同,每个控制器类应包含在一个分离的文件中。例如,采购模块在控制器/portal.py 中实现其客户门户的一部分,并在控制器/购买.py 上实现其自身的典型路由。 对于静态文件,由于资源可以在不同的上下文中使用(前端、后端、两者),因此它们将仅包含在一个捆绑包中。因此,CSS/Less、JavaScript 和 XML 文件应生捆绑一个其类型的后缀名称。 即:im_chat_common.css,用于"资产_公共"捆绑的 im_chat_common.js,以及用于"资产_后端"捆绑的 im_chat_backend.js。如果模块只拥有一个文件,则约定将<module_name>.ext(即:project.js)。不要在 Odoo 外部链接数据(图像、库):不要使用图像的 URL,而应将其复制到我们的代码库中。 关于数据,按用途拆分它们:数据或演示。文件名将是主模型名称,后缀为 _data.xml 或 _demo.xml。 关于向导,命名约定是 : <main_transient>.py <main_transient>_views.xml 其中<main_transient>是占主导地位的瞬态模型的名称,就像模型一样。<main_transient>.py 可以包含模型"transient_model.action"和"transient_model.action.line"。 统计报表命名: report/<report_name>_report.py report/<report_name>_report_views.py 可打印报表: report/<print_report_name>_reports.py report/<print_report_name>_templates.xml 完整的文件列表如下: addons/<my_module_name>/ |-- init.py |-- manifest.py |-- controllers/ | |-- init.py | |-- <inherited_module_name>.py | -- <my_module_name>.py |-- data/ | |-- <main_model>_data.xml | -- <inherited_main_model>_demo.xml |-- models/ | |-- init.py | |-- <main_model>.py | -- <inherited_main_model>.py |-- report/ | |-- __init__.py | |-- <main_stat_report_model>.py | |-- <main_stat_report_model>_views.xml | |-- <main_print_report>_reports.xml | -- <main_print_report>_templates.xml |-- security/ | |-- ir.model.access.csv | -- <main_model>_security.xml |-- static/ | |-- img/ | | |-- my_little_kitten.png | | -- troll.jpg | |-- lib/ | | -- external_lib/ | -- src/ | |-- js/ | | |-- <my_module_name>.js -- <my_widget_A>.js | |-- css/ | | -- <my_module_name>.css | |-- scss/ | | -- <my_module_name>.scss | -- xml/ | |-- <my_module_name>.xml -- <my_widget_A>.xml |-- views/ | |-- <main_model>_templates.xml | |-- <main_model>_views.xml | |-- <inherited_main_model>_templates.xml | -- <inherited_main_model>_views.xml -- wizard/ |-- <main_transient_A>.py |-- <main_transient_A>_views.xml |-- <main_transient_B>.py -- <main_transient_B>views.xml 文件名应仅包含 [a-z0-9] (小写字母数字和 _) 警告 使用正确的文件权限 : 文件夹 755 和文件 644。 XML文件 格式 当定义一个记录的xml时,需要标记: id属性放在model属性前 字段(field)定义中,name属性放在第一个。然后,将值放在标签内,或者放在eval属性中。最后将其他属性(widget、options...)按重要性排序。 尝试按模型对记录进行分组。如果操作/菜单/视图之间存在依赖关系,则此约定可能不适用。 使用在下一章节中定义的命名约定 标签仅用于设置不可更新的数据noupdate=1,如果整个xml文件都是不可更新数据,则noupdate=1属性可以设置在标签上,而不需要标签。 view.name object_name Odoo支持一些自定义标签作为快捷方式: menuitem:作为ir.ui.menu的快捷方式 template: 表示只需要arch视图部分的QWeb视图 report: 用于定义报表report action act_window:当record用不了的时候用它 我们更推荐上述4个标签类型,相对于 record 标签。 xml_id命名 权限(Security)、视图(View)和动作(Action)使用的命名规则: 菜单(menu): <model_name>_menu 视图(view): <model_name>view<view_type>,view_type可能的取值有:kanban, form, tree, search 动作(action): 主动作命名为<model_name>_action,其他的动作命名为<model_name>action,其中使用小写字母简单描述动作 组(group): <model_name>group<group_name>,group_name可能的取值包括:user, manager,... 规则(rule): <model_name>rule<concerned_group>,concerned_group可能的取值包括: 模型用户规则user, 公共用户规则public,多公司用户规则company ... ... ... ... ... ... ... 视图名称(name)使用点表示法:my.model.view_type 或者 my.model.view_type.inherit 继承XML的命名 继承视图的命名规则为 #. Extension 扩展模式:使用与要扩展的原始视图相同的 xml id,并使用 _inherit 后缀 :如 视图view project.project_view_form 被扩展 为 project_forecast.project_view_form_inherit. #. Primary 主模式:保持原有 xml id。 ... primary ... Python PEP8 选项 Odoo源代码基本准守Python标准PEP8,但是忽略其中一些规则: E501:行太长了 E301:预计有1个空行,找到0 E302:预计有2个空行,找到1 E126:延长线过度缩进以用于悬挂缩进 E123:关闭支架与开口支架线的压痕不匹配 E127:延伸线过度缩进以进行视觉缩进 E128:用于视觉缩进的缩进的延续线 E265:阻止评论应以'#'开头 Import 导入 import 的顺序 导入外部库 导入odoo 导入odoo的模块 在每组中的导入按字母顺序排序 1 : imports of python lib import base64 import re import time from datetime import datetime 2 : imports of odoo import odoo from odoo import api, fields, models # alphabetically ordered from odoo.tools.safe_eval import safe_eval as eval from odoo.tools.translate import _ 3 : imports from odoo addons from odoo.addons.website.models.website import slug from odoo.addons.web.controllers.main import login_redirect 编程习惯 每个python文件都应该以 # -- coding: utf-8 -- 作为第一行。 简单易读的代码 不要使用.clone() bad new_dict = my_dict.clone() new_list = old_list.clone() good new_dict = dict(my_dict) new_list = list(old_list) Odoo中编程 避免创建生成器和装饰器:仅使用Odoo API已有的 使用filtered, mapped, sorted, … 方法来提升代码可读性和性能。 让你的方法可以批量处理 当添加一个函数时,确保它可以处理多条数据,典型用法是通过 api.multi 装饰器,可以在self上进行循环处理 @api.multi def my_method(self) for record in self: record.do_cool_stuff() 避免使用 api.one 装饰器,因为它可能不会像预想中一样工作。 为了更好的性能,比如当定义一个状态按钮时,不在 api.multi 循环里用search 和 search_count 方法,要用 read_group 来实现一次请求中全部计算。 @api.multi def _compute_equipment_count(self): """ Count the number of equipement per category """ equipment_data = self.env['hr.equipment'].read_group([('category_id', 'in', self.ids)], ['category_id'], ['category_id']) mapped_data = dict([(m['category_id'][0], m['category_id_count']) for m in equipment_data]) for category in self: category.equipment_count = mapped_data.get(category.id, 0) context上下文环境 context是一个 frozendict 不能修改。当需要使用不同的 context 来调用一个方法时,可以通过with_context 来实现。 records.with_context(new_context).do_stuff() # all the context is replaced records.with_context(**additionnal_context).do_other_stuff() # additionnal_context values override native context ones 在 context 中传递参数可能会产生危险的副作用。由于值是自动传播的,因此可能会出现某些不可预期行为。在 context 调用具有 default_my_field 键的模型的 create() 方法将为相关模型设置 my_field 的默认值。但是,如果固化此创建,其他对象(如 sale.order.line,在sale.order 创建中)具有一个相同字段 my_field,其默认值也将设置。 如果需要创建影响某些对象行为的关键 context,请选择一个好名称,并最终按模块的名称为其前缀,以隔离其影响。一个很好的例子是 mail 模块的密钥:mail_create_nosubscribe, mail_notrack, mail_notify_user_signature, … 不要绕过ORM 当ORM可以实现的时候尽量使用ORM而不要直接写sql,因为它可能会绕过orm的一些规则如权限、事务等,还会让代码变得难读且不安全。 very very wrong self.env.cr.execute('SELECT id FROM auction_lots WHERE auction_id in (' + ','.join(map(str, ids))+') AND state=%s AND obj_price > 0', ('draft',)) auction_lots_ids = [x[0] for x in self.env.cr.fetchall()] no injection, but still wrong self.env.cr.execute('SELECT id FROM auction_lots WHERE auction_id in %s ' 'AND state=%s AND obj_price > 0', (tuple(ids), 'draft',)) auction_lots_ids = [x[0] for x in self.env.cr.fetchall()] better auction_lots_ids = self.search([('auction_id','in',ids), ('state','=','draft'), ('obj_price','>',0)]) 防止SQL注入! 使用 SQL 代码查询时,必须注意不要引入 SQL 注入漏洞。当用户输入被错误地过滤或引用错误时,存在此漏洞,从而允许攻击者向 SQL 查询引入不需要的子句(例如绕过筛选器或执行 UPDATE 或 DELETE 命令)。 安全的最佳方法是永远不要使用 Python 字符串串联 (+) 或字符串参数插值 (%)将变量传递到 SQL 查询字符串。 几乎同样重要的第二个原因,应该由数据库抽象层 (psycopg2) 来决定如何格式化查询参数,而不是您的代码!例如,psycopg2 知道,当您传递一个值列表时,它需要将它们格式化为逗号分隔列表,包含在括号中! the following is very bad: - it's a SQL injection vulnerability - it's unreadable - it's not your job to format the list of ids self.env.cr.execute('SELECT distinct child_id FROM account_account_consol_rel ' + 'WHERE parent_id IN ('+','.join(map(str, ids))+')') better self.env.cr.execute('SELECT DISTINCT child_id ' 'FROM account_account_consol_rel ' 'WHERE parent_id IN %s', (tuple(ids),)) 这些都是非常重要的,所以在重构时也要小心,最重要的是不要复制这类错误! 下面是一些令人难忘的示例,可帮助您记住问题的内容(但不要在那里复制代码)。在继续之前,请务必阅读 pyscopg2 的在线文档,以了解正确使用它: The problem with query parameters (http://initd.org/psycopg/docs/usage.html#the-problem-with-the-query-parameters) How to pass parameters with psycopg2 (http://initd.org/psycopg/docs/usage.html#passing-parameters-to-sql-queries) Advanced parameter types (http://initd.org/psycopg/docs/usage.html#adaptation-of-python-values-to-sql-types) 扩展的思考 函数和方法不应包含太多的逻辑:使用大量小而简单的方法比使用少量大型和复杂的方法更可取。一条好的经验法则是,只要方法具有多个责任,就将其拆分(参考 http://en.wikipedia.org/wiki/Single_responsibility_principle) 应避免在方法中硬编码业务逻辑,因为它可以防止子模块轻松扩展。 do not do this modifying the domain or criteria implies overriding whole method def action(self): ... # long method partners = self.env['res.partner'].search(complex_domain) emails = partners.filtered(lambda r: arbitrary_criteria).mapped('email') better but do not do this either modifying the logic forces to duplicate some parts of the code def action(self): ... partners = self._get_partners() emails = partners._get_emails() better minimum override def action(self): ... partners = self.env['res.partner'].search(self._get_partner_domain()) emails = partners.filtered(lambda r: r._filter_partners()).mapped('email') 为了示例,上述代码是可扩展的,但必须考虑可读性,并且必须做出权衡。 此外,相应地命名函数:小且正确命名的函数是可读/可维护代码和更严格的文档的起点。 此建议还与类、文件、模块和包相关。(另请参阅 http://en.wikipedia.org/wiki/Cyclomatic_complexity) 不要手动提交事务 Odoo 框架负责为所有 RPC 调用提供事务上下文。原则是,在每个 RPC 调用开始时,打开一个新的数据库游标,并在调用return返回时commit,即在commit前先将结果返回至 RPC 客户端,大致如下所示: def execute(self, db_name, uid, obj, method, *args, **kw): db, pool = pooler.get_db_and_pool(db_name) # create transaction cursor cr = db.cursor() try: res = pool.execute_cr(cr, uid, obj, method, *args, **kw) cr.commit() # all good, we commit except Exception: cr.rollback() # error, rollback everything atomically raise finally: cr.close() # always close cursor opened manually return res 如果在执行 RPC 调用期间发生任何错误,事务将以原方式回滚,从而保留系统的状态。 同样,系统在执行测试代码期间还提供专用事务,因此可以回滚或不依赖于服务器启动选项。 因此,如果您在任何地方代码调用 cr.commit() ,您很有可能会以各种方式破坏系统,因为您将导致部分提交,从而导致部分和不干净的回滚,从而导致问题: 业务数据不一致,通常是数据丢失 工作流取消同步,文档永久卡住 无法干净地回滚的测试,并且将开始污染数据库并触发错误(即使事务期间未发生错误也是如此) 所以请遵照以下非常简单的规则: 您绝对不要自己调用 cr.commit(),除非 您已显式创建自己的数据库游标!或者你有特殊的原因必须这样做。 如果您确实创建了自己的游标,则需要处理错误情况和适当的回滚,并在完成该游标后正确关闭游标。 与普遍的看法相反,在以下情况下,您甚至不需要调用cr.commit() : 在模型的 _auto_init() 方法中。模型对象:这是由加载项初始化方法,或由 ORM 事务在创建自定义模型时处理 在报表中: commit() 也由框架处理,因此您甚至可以在报表内更新数据库 在模型中。瞬态方法:这些方法与常规模型完全一样。模型,在事务中,并在末尾使用相应的 cr.commit()/rollback() 其它情况(如果您有疑问,请参阅上面的一般规则! 所以,服务器框架之外的所有 cr.commit() 调用都必须有一个明确的注释,解释它们为什么是绝对必要的,为什么它们确实是正确的,以及为什么它们不中断事务。否则,他们应该被删除! 正确的使用翻译方法 Odoo 使用类似于 GetText 的方法名 _( ) 来指示代码中使用的静态字符串需要在运行时使用上下文的语言进行翻译。通过在代码中通过导入访问此伪方法: from odoo.tools.translate import _ 在使用它时,必须遵循一些非常重要的规则,以便它正常工作,避免在翻译中填充无用的垃圾。 基本上,此方法只应用于在代码中手动编写的静态字符串,它无法转换字段值,如产品名称等。必须使用相应字段上的翻译标志来完成此操作。 规则非常简单:对下划线方法的调用应始终以 _("文本字符串")的形式进行,而没有其他内容: good: plain strings error = _('This record is locked!') good: strings with formatting patterns included error = _('Record %s cannot be modified!') % record ok too: multi-line literal strings error = _("""This is a bad multiline example about record %s!""") % record error = _('Record %s cannot be modified' 'after being validated!') % record bad: tries to translate after string formatting (pay attention to brackets!) This does NOT work and messes up the translations! error = _('Record %s cannot be modified!' % record) bad: dynamic string, string concatenation, etc are forbidden! This does NOT work and messes up the translations! error = _("'" + que_rec['question'] + "' n") bad: field values are automatically translated by the framework This is useless and will not work the way you think: error = _("Product %s is out of stock!") % _(product.name) and the following will of course not work as already explained: error = _("Product %s is out of stock!" % product.name) bad: field values are automatically translated by the framework This is useless and will not work the way you think: error = _("Product %s is not available!") % _(product.name) and the following will of course not work as already explained: error = _("Product %s is not available!" % product.name) Instead you can do the following and everything will be translated, including the product name if its field definition has the translate flag properly set: error = _("Product %s is not available!") % product.name 此外,请记住,翻译者必须使用传递给 _() 函数的字面值,因此请尽量使其易于理解,并将伪字符和格式保持在最低限度。翻译者必须注意,需要保留格式模式(如 %s 或 %d、newline 换行等),以明智和明显的方式使用这些格式模式非常重要: Bad: makes the translations hard to work with error = "'" + question + _("' nPlease enter an integer value ") Better (pay attention to position of the brackets too!) error = _("Answer to question %s is not valid.n" "Please enter an integer value.") % question 通常,在 Odoo 中,在操作字符串时,更多用 % 而不是用 .format()(当字符串中只有一个变量替换),更多用 %(varname) 而不是指定位置(当多个变量必须替换时)。这使得社区翻译人员更容易翻译。 符号和习惯 模型名-使用.分隔,模块名做前缀 定义odoo模型时,使用单数形式的名字如res.partner,sale.order 而不是 res.partnerS,saleS.orderS 定义odoo Transient / wizard时,命名格式为<related_base_model>.,此瞬态模型是 related_base_model是基础模型(在目录 models/ 中定义)进行的关联,action是功能简称,避免使用 wizard 关键字. 例如: account.invoice.make, project.task.delegate.batch, … 定义报表模型(或者SQL View)时,使用<related_base_model>.report.,和瞬态模型一样 python类-使用驼峰命名 class AccountInvoice(models.Model): ... 变量命名 模型变量使用驼峰命名方式 普通变量用小写字母 + 下划线关联来命名 如果变量包含记录 id 或 id 列表则将变量名称后缀为 _id 或 _ids。不要使用 partner_id 来包含一条 res.partner 的记录(id 只记录id这个数字,而非对象) Partner = self.env['res.partner'] partners = Partner.browse(ids) partner_id = partners[0].id One2Many, Many2Many字段一般以ids作为后缀如:sale_order_line_ids Many2One 字段一般以_id为后缀如:partner_id, user_id 方法命名 计算字段: 计算方法一般是_compute<field_name> 搜索方法: search<field_name> 默认方法: default<field_name> 列表值方法: selection<field_name> onchange方法: onchange<field_name> Constraint 约束方法: check<constraint_name> Action动作方法: 一个对象的动作方法一般以action_开头,它的装饰器是 @api.multi,如果它只使用单条计算,可在方法头添加self.ensure_one() 模型中属性顺序 私有属性:(_name, _description, _inherit, …) 默认方法和 _default_get Field 字段声明 Compute, inverse and search 等计算和搜索方法和字段声明顺序一致 Selection 方法(返回 selection 字段的列表值) Constrains 约束方法(@api.constrains) and onchange 字段值变更方法 (@api.onchange) CRUD方法(ORM 覆盖与继承) Action方法 最后是其他业务方法 class Event(models.Model): # Private attributes _name = 'event.event' _description = 'Event' # Default methods def _default_name(self): ... # Fields declaration name = fields.Char(string='Name', default=_default_name) seats_reserved = fields.Integer(oldname='register_current', string='Reserved Seats', store=True, readonly=True, compute='_compute_seats') seats_available = fields.Integer(oldname='register_avail', string='Available Seats', store=True, readonly=True, compute='_compute_seats') price = fields.Integer(string='Price') event_type = fields.Selection(string="Type", selection='_selection_type') # compute and search fields, in the same order of fields declaration @api.multi @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register') def _compute_seats(self): ... @api.model def _selection_type(self): return [] # Constraints and onchanges @api.constrains('seats_max', 'seats_available') def _check_seats_limit(self): ... @api.onchange('date_begin') def _onchange_date_begin(self): ... # CRUD methods (and name_get, name_search, ...) overrides def create(self, values): ... # Action methods @api.multi def action_validate(self): self.ensure_one() ... # Business methods def mail_user_confirm(self): ... Javascript和CSS Static files 静态文件目录 Odoo 模块有一些关于如何构造各种文件的约定。在这里,我们将更详细地解释 Web 资源应该如何组织。 首先要知道的是,Odoo 服务器将提供(静态)web服务给位于 static/ folder 文件夹中的所有文件,注意仅针对指定后缀的文件类型。一个例子是,如果文件位于 addons/web/static/src/js/some_file.js , 那么此静态文件可以如此访问 url your-odoo-server.com/web/static/src/js/some_file.js 请按如下约定结构组织你的代码: static: 所有一般静态资源文件 static/lib: 库文件,如: jquery 库可放在 addons/web/static/lib/jquery static/src: 源文件 static/src/css static/src/fonts static/src/img static/src/js static/src/scss static/src/xml: 所有qweb templates 文件 static/tests: 所有测试相关文件 Javascript 编码规范 use strict; 建议用于所有 javascript 文件 使用代码格式化脚本 linter (jshint, …) 不要使用压缩编译过的 Javascript 库 类名使用驼峰命名 除非你的javascript代码需要全局(即在所有页面)运行,否则必须在website模块中声明一个 if_dom_contains 。对 dom 元素的操作要使用 JQuery 代码 odoo.website.if_dom_contains('.jquery_class_selector', function () { /your code here/ }); CSS 编码规范 用 o_<module_name> foras forase,其中 module_name 是模块的技术名称("sale","im_chat",...)或模块的主路由(主要为 website 网站相关模块,如网站/论坛模块的"o_forum")。此规则的唯一例外是 webclient:它只使用 o_ 前缀。 避免使用 id 标签 使用Bootstrap 原生的 classes 使用 underscore 小写加 _ 连接来命名 class 类 Git 操作规范 这个按各自喜好,习惯方便就好,关键在于把 Git 用好!
  • odoo开箱即用: “采购运费”做两票结算并录入成本

    1
    7
    0 赞同
    1 帖子
    675 浏览
    2264082
    odoo开箱即用: “采购运费”做两票结算并录入成本 : 场景说明: 公司向 v1供货商购买 p1产品100个单价5元含税 (开v1供货商税票) ,运费15元含税由E1物流承运 (开E1物流税票),运费由v1供货商代垫,公司再和货款一起支付 [image: 1565142875987-2019-08-01_110447.png] [image: 1565142888267-2019-08-01_110554.png] [image: 1565142897689-2019-08-01_110751.png] [image: 1565142907263-2019-08-01_111005.png] [image: 1565142917203-2019-08-01_111046.png] [image: 1565142924362-2019-08-01_111047.png] [image: 1565142934808-2019-08-01_111438.png] 台北-226408 QQ = 3045661196 Mail= [email protected] odoo 实施"开箱即用"