Odoo中文社区可以通过以下三个域名访问:shine-it.net , odoocn.org,odoo.net.cn

原论坛用户的基本信息和发帖这里都予以保留,请注意:原论坛用户无需重新注册新用户,但是您的密码需要重置

开发人员可以登录gitter讨论组: http://gitter.im/odoo-china/Talk, 需要github账号

如果您登录系统碰到问题,请在微信公众号留言:

自制Openerp图表



  • 注意:<br />1. 本文介绍一种简单的,非通用的改进openerp的思路。并非一定要取代原有方式。<br />2. 本文会修改web_graph模块,如果在你的项目里使用了这个模块,请避免修改,以防止异常。<br />3. 本文基于openerp 6.1<br />通过本文,你可以知道:<br />1. web_graph的运行机制。<br />2. 如何动手修改这个模块。<br /><br />看看这个模块的结构:<br />[attachimg=1]<br /><br />客户端采用的是highchart(http://www.highcharts.com/),当然,如果你喜欢其他的lib,都是没问题的。<br /><br />第一步,把highcharts给包含到模块来,这样openerp才能把这个库合并输出。<br />把highcharts放置在适合的位置。<br /><br />修改__openerp__.py<br />[code]<br />{<br />    "name": "Graph Views",<br />    "category" : "Hidden",<br />    "description":"""Graph Views for Web Client<br /><br />* Parse a <graph> view but allows changing dynamically the presentation<br />* Graph Types: pie, lines, areas, bars, radar<br />* Stacked / Not Stacked for areas and bars<br />* Legends: top, inside (top/left), hidden<br />* Features: download as PNG or CSV, browse data grid, switch orientation<br />* Unlimited "Group By" levels (not stacked), two cross level analysis (stacked)<br />""",<br />    "version": "3.0",<br />    "depends": ['web'],<br />    "js": [<br />        "static/lib/highchart/js/highcharts.js",<br />        "static/src/js/graph.js"<br />    ],<br />    "css": [<br />        "static/src/css/.css",<br />    ],<br />    'qweb' : [<br />        "static/src/xml/.xml",<br />    ],<br />    "auto_install": True<br />}<br /><br />[/code]<br /><br /><br />下面研究highcharts.<br />观察highcharts的示例(http://www.highcharts.com/demo/),折线图是这样运行的:<br /><br />[code]<br />$(function () {<br />    var chart;<br />    $(document).ready(function() {<br />        chart = new Highcharts.Chart({<br />            chart: {<br />                renderTo: 'container',<br />                type: 'line',<br />                marginRight: 130,<br />                marginBottom: 25<br />            },<br />            title: {<br />                text: 'Monthly Average Temperature',<br />                x: -20 //center<br />            },<br />            subtitle: {<br />                text: 'Source: WorldClimate.com',<br />                x: -20<br />            },<br />            xAxis: {<br />                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',<br />                    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']<br />            },<br />            yAxis: {<br />                title: {<br />                    text: 'Temperature (°C)'<br />                },<br />                plotLines: [{<br />                    value: 0,<br />                    width: 1,<br />                    color: '#808080'<br />                }]<br />            },<br />            tooltip: {<br />                formatter: function() {<br />                        return '<b>'+ this.series.name +'</b><br/>'+<br />                        this.x +': '+ this.y +'°C';<br />                }<br />            },<br />            legend: {<br />                layout: 'vertical',<br />                align: 'right',<br />                verticalAlign: 'top',<br />                x: -10,<br />                y: 100,<br />                borderWidth: 0<br />            },<br />            series: [{<br />                name: 'Tokyo',<br />                data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]<br />            }, {<br />                name: 'New York',<br />                data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]<br />            }, {<br />                name: 'Berlin',<br />                data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]<br />            }, {<br />                name: 'London',<br />                data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]<br />            }]<br />        });<br />    });<br />    <br />});<br />[/code]<br /><br /><br />第二步,研究一下服务端。<br />服务端代码集中在graph.py里。<br />GraphView类是一个标准的View, 是客户端图表数据的来源,也定义了图表显示方式。如果要修改图表,几乎是要动这个类。<br />我们来简单的看。首先修改客户端。<br />只要把服务端返回的数据变成这种格式就可以了。动手:<br />[code]<br /># -- coding: utf-8 --<br /><br />import tools<br />from tools import safe_eval<br /><br />try:<br />    # embedded<br />    import openerp.addons.web.common.http as openerpweb<br />    from openerp.addons.web.controllers.main import View<br />except ImportError:<br />    # standalone<br />    import web.common.http as openerpweb<br />    from web.controllers.main import View<br /><br />from lxml import etree<br /><br />class GraphView(View):<br />    _cp_path = '/web_graph/graph'<br />    <br />    @tools.cache(timeout=3600)<br />    def from_db(self, obj, chart_type, title, fields, domain, group_by, context):<br />        result = {}<br />        if len(fields)<2:<br />            return result<br />        <br />        field_x = fields[1]<br />        field_y = fields[2]<br />        field_z = (len(fields)==4) and fields[3] or ''<br /><br />        ids = obj.search(domain)<br />            <br />        if ids:<br />            records = obj.read(ids)<br />            <br />            #field_x<br />            categories = []<br />            #field_z<br />            groups = []<br />            series = []<br />            <br />            if field_z:<br />                data_set = {}<br />                for r in records:<br />                    #get categories.<br />                    if r[field_x] not in categories:<br />                        categories.append(r[field_x])<br />                        <br />                    if r[field_z] not in groups:<br />                        groups.append(r[field_z])<br />                    <br />                    data_set[r[field_x]+r[field_z]] = r[field_y]<br />                <br />                #transform data<br />                # series<br /><br />                for g in groups:<br />                    s = {'name':g, 'data':[]}<br />                    for cate in categories:<br />                        s['data'].append(data_set.get(cate+g, 0))<br />                    series.append(s)<br /><br />            else:<br />                data = []<br />                for r in records:<br />                    if r[field_x] not in categories:<br />                        categories.append(r[field_x])<br />                    data.append(r[field_y])<br />                <br />                series.append({'data':data})<br /><br />        return categories, series<br />    <br />    @openerpweb.jsonrequest<br />    def data_get(self, req, model=None, domain=[], group_by=[], view_id=False, context={}, *kwargs):<br /><br />        obj = req.session.model(model)<br />        xml = obj.fields_view_get(view_id, 'graph')<br />        graph_xml = etree.fromstring(xml['arch'])<br />        <br />        chart_type = graph_xml.attrib.get('type') or 'line'<br />        chart_title = graph_xml.attrib.get('string') or '图表'<br />        fields = [ element.attrib.get('name') for element in graph_xml.iter() ]<br />        <br />        data = self.from_db(obj, chart_type, chart_title, fields, domain, group_by, context)<br /><br />        result = {<br />            'title':chart_title,<br />            'categories':data[0],<br />            'series':data[1],<br />            'chart_type':chart_type,<br />        }<br />        <br />        return result<br /><br />[/code]<br /><br />很简单, 我只处理这样的Graph定义:<br />[code]<br /><record id="view_sale_order_report_monthly_tree" model="ir.ui.view"><br />        <field eval="1" name="priority"/><br />        <field name="name">sale.order.report.monthly.tree</field><br />        <field name="model">sale.order.report.monthly</field><br />        <field name="type">tree</field><br />        <field name="arch" type="xml"><br />            <tree string="每月销售统计"><br />                <field name="date" /><br />                <field name="amount" /><br />                <field name="source" /><br />            </tree><br />        </field><br />    </record><br />[/code]<br /><br />第一个field,作为x轴,第二个,作为y轴。第三个,group成多个系列。 这样的处理就是简单化,并不会考虑openerp原来考虑的事情,所以不是一个通用方法。<br />(另,如果要是想进一步扩展graph,则需要修改addons/base/rng/view.rng的规则。)<br /><br />下面就是让highcharts显示出来了。<br />观察web_graph的xml模板和graph.js<br />我记得原来的xml模板element_id上有一些bug(6.1),我修改了一下:<br />[code]<br /><template><br />    <div t-name="GraphView" t-att-id="element_id+'-chart-'+chart_id"<br />        style="height:300px;position:relative;"/><br /></template><br />[/code]<br /><br />这是highcharts显示的容器。<br />重头戏在graph.js里,这里需要处理很多东西,但是也简单。按照原本的客户端views写法:<br /><br />[code]<br /><br />/---------------------------------------------------------<br /> * OpenERP web_graph<br /> ---------------------------------------------------------/<br /><br />openerp.web_graph = function (openerp) {<br /><br />var QWeb = openerp.web.qweb,<br />    _lt = openerp.web._lt;<br />openerp.web.views.add('graph', 'openerp.web_graph.GraphView');<br />openerp.web_graph.GraphView = openerp.web.View.extend({<br />    display_name: _lt('Graph'),<br />    <br />    init: function(parent, dataset, view_id, options) {<br />        this._super(parent);<br />        this.dataset = dataset;<br />        this.view_id = view_id;<br />        this.set_default_options(options);<br />        this.fields_view = {};<br />        <br />        this.model = dataset.model;<br />        this.chart_id = Math.floor((Math.random()100)+1);<br />    },<br />    <br />    start: function() {<br />        this._super();<br />        <br />        this.$element.html(QWeb.render("GraphView", {<br />            "chart_id": this.chart_id,<br />            'element_id': this.widget_parent.element_id<br />        }));<br />    },<br />    stop: function() {<br />        this._super();<br />    },<br /><br />    /<br />    * get data here.<br />    */<br />    do_search: function(domain, context, group_by) {<br />        <br />        this.rpc(<br />                  '/web_graph/graph/data_get',<br />                  {<br />                      'model': this.model,<br />                      'domain': domain,<br />                      'group_by': group_by,<br />                      'view_id': this.view_id,<br />                      'context': context<br />                  }, this.on_search<br />                );<br /><br />    },<br />    <br />    on_search: function(result){<br />        container = this.widget_parent.element_id+"-chart-"+this.chart_id;<br />        <br />        var chart = new Highcharts.Chart({<br />            chart: {<br />                renderTo: container,<br />                height: 300<br />            },<br />            title: {<br />                text: result.title<br />            },<br />            xAxis: {<br />                categories: result.categories<br />            },<br />            series: result.series<br />        });<br />    },<br />    <br />    do_show: function() {<br />        this.do_push_state({});<br />        return this.super();<br />    }<br />});<br />};<br />// vim:et fdc=0 fdl=0:<br /><br />[/code]<br /><br />能看出,主要是三个方法(标准的api,参考openerp客户端的文档):<br />start, do_search, on_search<br /><br />start是指本widget启动的时候要做的事情。<br />do_search, 是启动以后,获取数据。<br />on_search, 服务端返回数据,根据数据来设置highchart, 显示数据。<br /><br />需要注意的是,模板里的element_id( t-att-id="element_id+'-chart-'+chart_id" ) 要和 graph.js里的<br />"container = this.widget_parent.element_id+"-chart-"+this.chart_id; " 一致。<br /><br /><br />最终,我们得到一个更好看的chart.<br /><br />[attachimg=2]<br /><br /><br />继续:<br />1. 可以写一个更通用的服务端。<br />2. 可以扩展这个chart,主要对view.rng里规则的修改。<br /><br /><br />心情不好,写的很笼统,如果有疑问,可以回帖,或者新浪微博 @杨振宇  <br />谨以此文,纪念我远在天堂的儿子。 愿天父的慈爱永远呵护你。



  • 注意:<br />1. 本文介绍一种简单的,非通用的改进openerp的思路。并非一定要取代原有方式。<br />2. 本文会修改web_graph模块,如果在你的项目里使用了这个模块,请避免修改,以防止异常。<br />3. 本文基于openerp 6.1<br />通过本文,你可以知道:<br />1. web_graph的运行机制。<br />2. 如何动手修改这个模块。<br /><br />看看这个模块的结构:<br />[attachimg=1]<br /><br />客户端采用的是highchart(http://www.highcharts.com/),当然,如果你喜欢其他的lib,都是没问题的。<br /><br />第一步,把highcharts给包含到模块来,这样openerp才能把这个库合并输出。<br />把highcharts放置在适合的位置。<br /><br />修改__openerp__.py<br />[code]<br />{<br />    "name": "Graph Views",<br />    "category" : "Hidden",<br />    "description":"""Graph Views for Web Client<br /><br />* Parse a <graph> view but allows changing dynamically the presentation<br />* Graph Types: pie, lines, areas, bars, radar<br />* Stacked / Not Stacked for areas and bars<br />* Legends: top, inside (top/left), hidden<br />* Features: download as PNG or CSV, browse data grid, switch orientation<br />* Unlimited "Group By" levels (not stacked), two cross level analysis (stacked)<br />""",<br />    "version": "3.0",<br />    "depends": ['web'],<br />    "js": [<br />        "static/lib/highchart/js/highcharts.js",<br />        "static/src/js/graph.js"<br />    ],<br />    "css": [<br />        "static/src/css/.css",<br />    ],<br />    'qweb' : [<br />        "static/src/xml/.xml",<br />    ],<br />    "auto_install": True<br />}<br /><br />[/code]<br /><br /><br />下面研究highcharts.<br />观察highcharts的示例(http://www.highcharts.com/demo/),折线图是这样运行的:<br /><br />[code]<br />$(function () {<br />    var chart;<br />    $(document).ready(function() {<br />        chart = new Highcharts.Chart({<br />            chart: {<br />                renderTo: 'container',<br />                type: 'line',<br />                marginRight: 130,<br />                marginBottom: 25<br />            },<br />            title: {<br />                text: 'Monthly Average Temperature',<br />                x: -20 //center<br />            },<br />            subtitle: {<br />                text: 'Source: WorldClimate.com',<br />                x: -20<br />            },<br />            xAxis: {<br />                categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',<br />                    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']<br />            },<br />            yAxis: {<br />                title: {<br />                    text: 'Temperature (°C)'<br />                },<br />                plotLines: [{<br />                    value: 0,<br />                    width: 1,<br />                    color: '#808080'<br />                }]<br />            },<br />            tooltip: {<br />                formatter: function() {<br />                        return '<b>'+ this.series.name +'</b><br/>'+<br />                        this.x +': '+ this.y +'°C';<br />                }<br />            },<br />            legend: {<br />                layout: 'vertical',<br />                align: 'right',<br />                verticalAlign: 'top',<br />                x: -10,<br />                y: 100,<br />                borderWidth: 0<br />            },<br />            series: [{<br />                name: 'Tokyo',<br />                data: [7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6]<br />            }, {<br />                name: 'New York',<br />                data: [-0.2, 0.8, 5.7, 11.3, 17.0, 22.0, 24.8, 24.1, 20.1, 14.1, 8.6, 2.5]<br />            }, {<br />                name: 'Berlin',<br />                data: [-0.9, 0.6, 3.5, 8.4, 13.5, 17.0, 18.6, 17.9, 14.3, 9.0, 3.9, 1.0]<br />            }, {<br />                name: 'London',<br />                data: [3.9, 4.2, 5.7, 8.5, 11.9, 15.2, 17.0, 16.6, 14.2, 10.3, 6.6, 4.8]<br />            }]<br />        });<br />    });<br />    <br />});<br />[/code]<br /><br /><br />第二步,研究一下服务端。<br />服务端代码集中在graph.py里。<br />GraphView类是一个标准的View, 是客户端图表数据的来源,也定义了图表显示方式。如果要修改图表,几乎是要动这个类。<br />我们来简单的看。首先修改客户端。<br />只要把服务端返回的数据变成这种格式就可以了。动手:<br />[code]<br /># -- coding: utf-8 --<br /><br />import tools<br />from tools import safe_eval<br /><br />try:<br />    # embedded<br />    import openerp.addons.web.common.http as openerpweb<br />    from openerp.addons.web.controllers.main import View<br />except ImportError:<br />    # standalone<br />    import web.common.http as openerpweb<br />    from web.controllers.main import View<br /><br />from lxml import etree<br /><br />class GraphView(View):<br />    _cp_path = '/web_graph/graph'<br />    <br />    @tools.cache(timeout=3600)<br />    def from_db(self, obj, chart_type, title, fields, domain, group_by, context):<br />        result = {}<br />        if len(fields)<2:<br />            return result<br />        <br />        field_x = fields[1]<br />        field_y = fields[2]<br />        field_z = (len(fields)==4) and fields[3] or ''<br /><br />        ids = obj.search(domain)<br />            <br />        if ids:<br />            records = obj.read(ids)<br />            <br />            #field_x<br />            categories = []<br />            #field_z<br />            groups = []<br />            series = []<br />            <br />            if field_z:<br />                data_set = {}<br />                for r in records:<br />                    #get categories.<br />                    if r[field_x] not in categories:<br />                        categories.append(r[field_x])<br />                        <br />                    if r[field_z] not in groups:<br />                        groups.append(r[field_z])<br />                    <br />                    data_set[r[field_x]+r[field_z]] = r[field_y]<br />                <br />                #transform data<br />                # series<br /><br />                for g in groups:<br />                    s = {'name':g, 'data':[]}<br />                    for cate in categories:<br />                        s['data'].append(data_set.get(cate+g, 0))<br />                    series.append(s)<br /><br />            else:<br />                data = []<br />                for r in records:<br />                    if r[field_x] not in categories:<br />                        categories.append(r[field_x])<br />                    data.append(r[field_y])<br />                <br />                series.append({'data':data})<br /><br />        return categories, series<br />    <br />    @openerpweb.jsonrequest<br />    def data_get(self, req, model=None, domain=[], group_by=[], view_id=False, context={}, *kwargs):<br /><br />        obj = req.session.model(model)<br />        xml = obj.fields_view_get(view_id, 'graph')<br />        graph_xml = etree.fromstring(xml['arch'])<br />        <br />        chart_type = graph_xml.attrib.get('type') or 'line'<br />        chart_title = graph_xml.attrib.get('string') or '图表'<br />        fields = [ element.attrib.get('name') for element in graph_xml.iter() ]<br />        <br />        data = self.from_db(obj, chart_type, chart_title, fields, domain, group_by, context)<br /><br />        result = {<br />            'title':chart_title,<br />            'categories':data[0],<br />            'series':data[1],<br />            'chart_type':chart_type,<br />        }<br />        <br />        return result<br /><br />[/code]<br /><br />很简单, 我只处理这样的Graph定义:<br />[code]<br /><record id="view_sale_order_report_monthly_tree" model="ir.ui.view"><br />        <field eval="1" name="priority"/><br />        <field name="name">sale.order.report.monthly.tree</field><br />        <field name="model">sale.order.report.monthly</field><br />        <field name="type">tree</field><br />        <field name="arch" type="xml"><br />            <tree string="每月销售统计"><br />                <field name="date" /><br />                <field name="amount" /><br />                <field name="source" /><br />            </tree><br />        </field><br />    </record><br />[/code]<br /><br />第一个field,作为x轴,第二个,作为y轴。第三个,group成多个系列。 这样的处理就是简单化,并不会考虑openerp原来考虑的事情,所以不是一个通用方法。<br />(另,如果要是想进一步扩展graph,则需要修改addons/base/rng/view.rng的规则。)<br /><br />下面就是让highcharts显示出来了。<br />观察web_graph的xml模板和graph.js<br />我记得原来的xml模板element_id上有一些bug(6.1),我修改了一下:<br />[code]<br /><template><br />    <div t-name="GraphView" t-att-id="element_id+'-chart-'+chart_id"<br />        style="height:300px;position:relative;"/><br /></template><br />[/code]<br /><br />这是highcharts显示的容器。<br />重头戏在graph.js里,这里需要处理很多东西,但是也简单。按照原本的客户端views写法:<br /><br />[code]<br /><br />/---------------------------------------------------------<br /> * OpenERP web_graph<br /> ---------------------------------------------------------/<br /><br />openerp.web_graph = function (openerp) {<br /><br />var QWeb = openerp.web.qweb,<br />    _lt = openerp.web._lt;<br />openerp.web.views.add('graph', 'openerp.web_graph.GraphView');<br />openerp.web_graph.GraphView = openerp.web.View.extend({<br />    display_name: _lt('Graph'),<br />    <br />    init: function(parent, dataset, view_id, options) {<br />        this._super(parent);<br />        this.dataset = dataset;<br />        this.view_id = view_id;<br />        this.set_default_options(options);<br />        this.fields_view = {};<br />        <br />        this.model = dataset.model;<br />        this.chart_id = Math.floor((Math.random()100)+1);<br />    },<br />    <br />    start: function() {<br />        this._super();<br />        <br />        this.$element.html(QWeb.render("GraphView", {<br />            "chart_id": this.chart_id,<br />            'element_id': this.widget_parent.element_id<br />        }));<br />    },<br />    stop: function() {<br />        this._super();<br />    },<br /><br />    /<br />    * get data here.<br />    */<br />    do_search: function(domain, context, group_by) {<br />        <br />        this.rpc(<br />                  '/web_graph/graph/data_get',<br />                  {<br />                      'model': this.model,<br />                      'domain': domain,<br />                      'group_by': group_by,<br />                      'view_id': this.view_id,<br />                      'context': context<br />                  }, this.on_search<br />                );<br /><br />    },<br />    <br />    on_search: function(result){<br />        container = this.widget_parent.element_id+"-chart-"+this.chart_id;<br />        <br />        var chart = new Highcharts.Chart({<br />            chart: {<br />                renderTo: container,<br />                height: 300<br />            },<br />            title: {<br />                text: result.title<br />            },<br />            xAxis: {<br />                categories: result.categories<br />            },<br />            series: result.series<br />        });<br />    },<br />    <br />    do_show: function() {<br />        this.do_push_state({});<br />        return this.super();<br />    }<br />});<br />};<br />// vim:et fdc=0 fdl=0:<br /><br />[/code]<br /><br />能看出,主要是三个方法(标准的api,参考openerp客户端的文档):<br />start, do_search, on_search<br /><br />start是指本widget启动的时候要做的事情。<br />do_search, 是启动以后,获取数据。<br />on_search, 服务端返回数据,根据数据来设置highchart, 显示数据。<br /><br />需要注意的是,模板里的element_id( t-att-id="element_id+'-chart-'+chart_id" ) 要和 graph.js里的<br />"container = this.widget_parent.element_id+"-chart-"+this.chart_id; " 一致。<br /><br /><br />最终,我们得到一个更好看的chart.<br /><br />[attachimg=2]<br /><br /><br />继续:<br />1. 可以写一个更通用的服务端。<br />2. 可以扩展这个chart,主要对view.rng里规则的修改。<br /><br /><br />心情不好,写的很笼统,如果有疑问,可以回帖,或者新浪微博 @杨振宇  <br />谨以此文,纪念我远在天堂的儿子。 愿天父的慈爱永远呵护你。



  • 阿门


  • 管理员

    写得很好,一个OpenERP web 开发的最佳实践, <br />and 节哀。



  • 阿门



  • 谢谢楼主无私贡献 ~ 拜读之后很受启发!<br /><br />现在看到这篇文章,相信您已经度过最艰难的时刻。<br /><br />逝者长已矣,来者犹可追。<br /><br />默哀并祝福楼主 ~


  • 管理员

    坛子里对OpenERP Web端的技术讨论不多,兄弟的帖子让我们受益匪浅。<br />6.1对OpenERP的web client做了完全的重写,变成了OpenERP应用服务器的一个addon了,并且可以象开发服务器端的功能模块一样开发web端的模块:[url=http://doc.openerp.com/trunk/developers/web/addons/]http://doc.openerp.com/trunk/developers/web/addons/[/url]<br />兄弟如果能将其做成一个addon模块,就是一个经典的学习案例了。<br /><br />兄弟教会了我什么是坚强,祝福你和你至爱的人。<br />



  • 感谢各位兄弟。<br /><br />说说为什么谈论web addon的少。<br />要想从技术上搞懂openerp这套体系,不仅仅需要python的基础,还需要很强的javascript的技术。<br />这在其他的领域,是那种前台后台通吃的人。<br />我恰好都做过一些。所以动手改的话,比较熟悉一些。<br /><br />我本人是在一家比较大的制造型企业里,做市场和品牌运作的工作。平时只有在闲的时候才能写一些东西。<br /><br />至于各位兄弟说的一个单独的chart addon, 过了这几天,我会考虑单独做一个出来。<br /><br />社区里这位“辛巴达”的兄弟: 我帮不了忙,因为我平时的事情太多,是在是不好意思。<br />


登录后回复
 

与 Odoo 中文社区 的连接断开,我们正在尝试重连,请耐心等待