ODOO集成Raphaël可视化销控视图
Raphaël 是一个用于在网页中绘制矢量图形的 Javascript 库。它使用 SVG W3C 推荐标准和 VML 作为创建图形的基础,你可以通过 JavaScript 操作 DOM 来轻松创建出各种复杂的柱状图、饼图、曲线图等各种图表,还可以绘制任意形状的图形,可以进行图表或图像的裁剪和旋转等复杂操作
虽然 D3.js 已经非常优秀,但多一个选择也不是坏事,何况理论上各种优秀的前端图形库都可以按照 ODOO 视图规范兼容共处一起飞:)
ODOO集成Raphaël可视化销控视图
Raphaël 是一个用于在网页中绘制矢量图形的 Javascript 库。它使用 SVG W3C 推荐标准和 VML 作为创建图形的基础,你可以通过 JavaScript 操作 DOM 来轻松创建出各种复杂的柱状图、饼图、曲线图等各种图表,还可以绘制任意形状的图形,可以进行图表或图像的裁剪和旋转等复杂操作
虽然 D3.js 已经非常优秀,但多一个选择也不是坏事,何况理论上各种优秀的前端图形库都可以按照 ODOO 视图规范兼容共处一起飞:)
ODOO模态对话框窗口拖动
年年一个大版本推进,但其前端基础库还是很古老的Bootstrap + jQuery + Underscore组合,11版本虽然对View层进行了激进的重构,也还是基于这个基架之上。同理jQuery UI交互界面库也一脉相承的延续至今,这就可以直接调用其Draggable Widget部件以最小的代价扩展原生模态对话框的拖拽功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/** * author: i@renjie.me */ var Dialog = require('web.Dialog'); Dialog.include({ open: function() { var self = this; var ret = self._super.apply(self, arguments); self.$modal.draggable({handle: ".modal-header"}); return ret; }, close: function() { var self = this; if(self.$modal.draggable("instance")) { self.$modal.draggable("destroy"); } self._super.apply(self, arguments); } }); |
ODOO社区版切换左侧导航菜单
社区版的菜单体系是一二三级同时分别展示于顶部和左侧,非常的直观,习惯这点后用企业版的菜单系统是非常难受的。个人认为最好的体验因该是大屏幕设备用社区版菜单,小屏幕终端用企业版菜单,关于这个课题以后在深入探讨,先回归本次主题:
1 2 3 4 5 6 7 |
<template id="webclient_bootstrap" inherit_id="web.webclient_bootstrap" name="renjie webclient_bootstrap"> <xpath expr="//nav" position="inside"> <div class="menu-toggle"> <a href="/" title="切换子菜单" class="fa fa-exchange"></a> </div> </xpath> </template> |
以上通过继承菜单对应的模版在导航区植入一个切换开关按扭的入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* author: i@renjie.me */ .navbar{ .menu-toggle{ position: absolute; top: 6px; left: 1px; a{ color: @navbar-inverse-link-color; &:hover, &:focus { background-color: @navbar-inverse-link-hover-bg; color: @nav-pills-active-link-hover-color; } } } } |
以上LESS代码定义其样式外观与一级菜单和谐保持一致并定位到合适的位置,没错,导航最左也就是第一个菜单前面的空位也就是它了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * author: i@renjie.me */ var WebClient = require('web.WebClient'); WebClient.include({ show_application: function() { var self = this; var ret = self._super.apply(self, arguments); self.$('nav .menu-toggle').click(self.proxy(self.toggle_menu)); return ret; }, toggle_menu: function(e) { var self = this; e.preventDefault(); self.$('.o_main .o_sub_menu').toggle("fast"); return false; } }); |
最后绑定上前端行为代码就可以开关自如的控制左侧导航区,同时还有一个小小的切换动画效果。至此,社区版也可以像企业版那样间接实现整个内容区都可以用来展示视图数据,特别适合那些数据多到需要用水平滚动条来左右拖动配合使用的场景
ODOO列表视图X对多字段显示(n 条记录)问题
考虑到性能和显示的原因,原生One2many多年来一直以条数内容简单显示于固定表格中,其中最为瑕疵的就是无相关数据时的0条记录,这个Many2many也一脉相承,不知道会不会成为洁癖爱好者们心中的痛:)
今天终于等到一个机会忍无可忍精炼代码一并解决名称显示和零记录问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/** * author: i@renjie.me */ var core = require('web.core'); var Model = require('web.DataModel'); var ListView = require('web.ListView'); var ColumnMany2Many = core.list_widget_registry.get('field.many2many'); ColumnMany2Many.include({ _format: function (row_data, options) { var self = this; if (_.isEmpty(row_data[this.id].value)) { return ''; }else{ row_data[this.id] = row_data[this.id + '__display']; } return self._super.apply(self, arguments); } }); core.list_widget_registry.add('field.one2many', ColumnMany2Many); ListView.List.include({ render_cell: function (record, column) { var self = this; if (column.type === 'one2many') { var value = record.get(column.id); if (value instanceof Array && !_.isEmpty(value) && !record.get(column.id + '__display')) { new Model(column.relation).call('name_get', [ value, self.dataset.get_context() ]).done(function (names) { record.set(column.id + '__display', _(names).pluck(1).join(', ')); record.set(column.id, value); }); record.set(column.id + '__display', false); } return column.format(record.toForm().data, { model: self.dataset.model, id: record.get('id') }); }else{ return self._super.apply(self, arguments); } } }); |
ODOO集成D3.js可视化位置尺寸状态视图
10版本开始视图根据multi_record布尔属性分为多记录和单记录两种,前者以列表为代表的大部分视图,后者则是表单为经典的单一视图以及神秘的Diagram视图
虽然原生的视图可以满足大部分需求,但是一些特殊的场景用专业的视图展现可以达到事半功倍的效果,如销控图、拓扑图、树状图等
D3.js是一个非常优秀的前端可视化库,其数据驱动的理念与ODOO完美互补:前者负责美丽娇艳,后者提供关系数据:)
ODOO QWeb报表黑色实线边框表格
原生报表表格的默认边框如下:
除了抬头一横条黑线外,剩下的灰线在针式打印下约等于无,非常没有整体感
根据Bootstrap标准和Less规范精炼了一段CSS预处理代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* author: i@renjie.me */ .table-bordered{ border: 1px solid black; > thead, > tbody, > tfoot{ > tr{ > th, > td{ border: 1px solid black; } } } } .table-condensed{ .table-bordered } |
可以直接添加到源码addons/report/static/src/less/report.less最后,也可以通过间接继承report.assets_common模板视图的根位置插入一个link元素less新文件
ODOO表单视图动作编辑模式打开
表单有两种模式,分别是view浏览和edit编辑,一般通过动作跳转的默认都是浏览模式,需要额外点击编辑按扭切换成编辑模式,对一些要求快速编辑的场景来说显得有些多余
ODOO10截止目前的版本已经可以直接通过action flags对象来透传initial_mode参数,具体用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
''' author: i@renjie.me ''' @api.multi def action_edit_partner(self): self.ensure_one() return { 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'res_model': 'res.partner', 'res_id': 1, 'context': self.env.context, 'flags': {'initial_mode': 'edit'}, } |
早期的版本虽然不支持,但是对于dict类型来说,也是可以通过return透传给前端拿到,只需额外添加一个通用的桥接口中转下即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * author: i@renjie.me */ var FormView = require('web.FormView'); FormView.include({ init : function(parent, dataset, views, flags, options){ var self = this; options = options || {}; if(flags && flags.initial_mode){ options.initial_mode = flags.initial_mode; } self._super.apply(self, arguments); } }); |
ODOO列表视图动态列
由于框架模型限定了字段是需要事先定义的,也就是个数是确定的,对应成列表视图仅仅是显示或者不显示这些字段而已
一些特殊的场景,比如工时单记录,根据项目在列表视图上一行行的填写工时就变得非常重复和多余,可惜官方没有改造列表视图使其获得通用能力,而是专门为之新开发了一个项目日期横排组件级视图,使得应用范围比较窄,多些固定列或者非时间动态列的情况下就无法直接使用了
一些行业的应用,比如服装行业的尺码横排,动态列一般是浮点型价格或者整型数量,而固定列除了颜色之外,一般还有批次、仓位、款式、订单等组合。前期也参考了工时单和Grid视图的优秀设计,甚至尝试基于其进行二次开发改良,但最终还是选择最普遍的原生列表视图为基础进行扩展,希望最终用户可以获得习惯一致的体验,同时也对经典视图深入发展多元化应用进行了一次实打实的尝试
ODOO邮件发送集成
电子邮件这个场景在国外集成率非常高,国内则相对少很多。一是因为文化习惯,而且我们还有微信、钉钉;二是因为Mail模块是针对国际化普世标准设计的,如Gmail、Postfix,不太适合中国大部分中小企业还在用腾讯、网易企业邮箱服务的国情
所以新手在中国要完美使用邮件集成是折腾的,这里我们先将问题一分为二,按照顺序从发送开始说,理解透彻了发送模式对后续接收及分发原理会有一定的帮助
相关系统参数:
mail.bounce.alias(默认值bounce,无界面设置)
mail.catchall.domain(默认值odoo域名,可以在通用设置别名域里设置)
发送账号配置:
默认localhost用于本地自建smtp邮件发送服务器使用,如Postfix服务
一般配置成企业邮箱专门为之新建的一个ODOO账号,如odoo@renjie.me
发件名义原则:
1、系统邮件如新用户邀请、密码重置等以公司Email设置里的邮箱名义发送,如info@renjie.me
2、个人邮件如操作者自己的业务沟通,以用户对应的联系人Email设置里的邮箱名义发送,如i@renjie.me
三大发送模式:
1、简单发送(mail.catchall.domain不设置)
直接用发送账号发出,发件人需要与发送账号相同或者是其同一账号下的其他别名,否则会被邮件系统拒绝,如SMTPSenderRefused: 501 mail from address must be same as authorization user test@renjie.me。接收方也直接回复该真实地址,非常容易理解,属于最原始的模式,最适合做邮件通知这种只集成发送不用集成接收的情况
2、代理发送(mail.catchall.domain设置,mail.bounce.alias不设置)
间接用postmaster-odoo账号代发出,发送账号需要具有该名称授权,否则也会被邮件系统拒绝,如SMTPSenderRefused: 501 mail from address must be same as authorization user postmaster-odoo@renjie.me。接收方默认回复catchall专用地址,由于属于代发,且发件地址、代发地址、回复地址都可以不相同,第一次使用还会有些颠覆邮箱观,甚至某些客户端出于谨慎还会出现欺诈提醒,但这确是目前国内用的最多的务实模式,大部分教程都是以此展开详解
3、反弹发送(mail.catchall.domain设置,mail.bounce.alias设置)
间接用bounce地址代发出,如bounce+5-res.users-2@renjie.me或者bounce+5@renjie.me,其中+5为邮件流水号,-2为单据流水号,可见前者还是单据相关邮件,接收方也默认回复catchall专用地址,因为其涉及到邮件接收等相关知识,而且国内免费邮箱服务基本没有完美支持,本文将不深入探讨,待以后ODOO邮件接收集成的时候在讨论如何配合Postfix实现完美收发自如
ODOO文档管理系统
自从开始玩上官方应用市场,一直坚持平均一个月推出一款应用,日积月累也慢慢形成了移动互联网流行的应用矩阵,同时也对海外客户的国际化需求和口味风格异同都有了不少的了解
今天突然发现上个月发布的一款文档管理系统今天已经进入下载排名十强,目前位于第八个的样子,这可是破记录了,因为之前最高也就是在第二页徘徊而已。曾经也羡慕过那些霸榜应用,现在看来只要坚持总会摸索到爆款痛点:)
论市场优秀应用,我认为至少要同步官方的节奏,不仅要支持最新的三个版本,更要兼容企业版,当然最重要的还是开源免费,而开发者所收获的则是国际的规范的扎实的基础能力
https://apps.odoo.com/apps/modules/10.0/document_management_system/