osbzr/gooderp_addons

View on GitHub
core/static/src/js/core.js

Summary

Maintainability
C
1 day
Test Coverage
odoo.define('core.core', function (require) {
    "use strict";
    var ListView = require('web.ListView');
    var common = require('web.list_common');
    var form_relational = require('web.form_relational');
    var data = require('web.data');
    var UserMenu = require('web.UserMenu');
    var Menu = require('web.Menu');
    var session = require('web.session');
    var Model = require('web.Model');
    var FormView = require('web.FormView');
    var PivotView = require('web.PivotView');
    var WebClient = require('web.AbstractWebClient');
    var formats = require('web.formats');
    var data_manager = require('web.data_manager');
    var core = require('web.core');
    var _t = core._t;
    var FieldBinaryFile = core.form_widget_registry.get('binary');
    var utils = require('web.utils');
    var Widget = require('web.Widget');
    /*
    One2many字段增加复制按钮
    */
    ListView.List.include({
        /** 绑定事件,监控复制按钮被点击 */
        init: function () {
            this._super.apply(this, arguments);
            var self = this;
            self.$current = self.$current.delegate('td.oe_list_record_copy', 'click', function (e) {
                e.stopPropagation();
                var $target = $(e.target),
                    $row = $target.closest('tr'),
                    record_id = self.row_id($row),
                    record = self.records.get(record_id);
                self.view.do_copy_record(record);
            });
        },
        pad_table_to: function (count) {
            count -=3;    /* 表格预留空行太多很难看 */
            if (this.records.length >= count ||
                _(this.columns).any(function (column) { return column.meta; })) {
                return;
            }
            /** 增加复制按钮 */
            var cells = [];
            if (this.options.deletable) {
                cells.push('<td class="copy_record"></td>');
            }
            if (this.options.selectable) {
                cells.push('<td class="o_list_record_selector"></td>');
            }
            _(this.columns).each(function (column) {
                if (column.invisible === '1') {
                    return;
                }
                cells.push('<td title="' + column.string + '">&nbsp;</td>');
            });
            if (this.options.deletable) {
                cells.push('<td class="o_list_record_delete"></td>');
            }
            cells.unshift('<tr>');
            cells.push('</tr>');

            var row = cells.join('');
            this.$current
                .children('tr:not([data-id])').remove().end()
                .append(new Array(count - this.records.length + 1).join(row));
        },
    });

    ListView.include({
        /** 在form中的tree列表行上前添加复制的按钮 以方便创建相同的行 */
        make_empty_record_copy: function (copy_recored) {
            var attrs = { id: false };
            _(this.columns).chain()
                .filter(function (x) { return x.tag === 'field'; })
                .pluck('name')
                .each(function (field) { attrs[field] = copy_recored[field]; });
            return new common.Record(attrs);
        },
        do_add_record: function () {
            var self = this;
            if (self.copy_recored === undefined) {
                this._super.apply(this, arguments);
            } else {
                if (this.editable()) {
                    this.$('table:first').show();
                    this.$('.oe_view_nocontent').remove();
                    var recored = this.make_empty_record_copy(self.copy_recored.attributes);
                    this.records.add(recored, { at: (this.prepends_on_create()) ? 0 : null });
                    this.start_edition(recored, undefined);
                } else {
                    this._super.apply(this, arguments);
                }
            }
        },
        do_copy_record: function (record) {
            var self = this;
            var def = self.save_edition();
            self.copy_recored = record;
            $.when(def).done(self.do_add_record.bind(self.x2m));
            self.copy_recored = undefined;
        }
    });

    /**
    使用 options="{'color':'random'}" 来实现多对多控件标签显示随机颜色
    */
    form_relational.FieldMany2ManyTags.include({
        render_tag: function (data) {
            if (this.options.color && this.options.color == 'random') {
                data = _.each(data, function (data_one, index) {
                    if (!data_one.color) {
                        data_one.color = index % 9 + 1;
                    }
                    return data_one;
                });
            }
            this._super.apply(this, arguments);
        },
    });

    /**
     * 2016-11-15 开阖静静(gilbert@osbzr.com)
     * 把设置默认值的的按钮菜单 放到form菜单的更多里面。
     * 2017-12-21 Sam(lgz.sam@qq.com)
     * form view中有input设置autofoucs=true,优先设置input获取焦点
    */
    FormView.include({
       render_sidebar: function($node) {
           this._super.apply(this, arguments);
           if(this.sidebar){
                this.sidebar.add_items('other', _.compact([
                   { label: '设默认值', callback: this.on_click_set_defaults}
                ]));
                this.sidebar.appendTo($node);
                this.toggle_sidebar();
           }
        },
        on_click_set_defaults:function() {
            this.open_defaults_dialog();
        },

        autofocus: function() {
            this._super.apply(this, arguments);

            var input_widgets = $(':input')
            for (var i = 0; i < input_widgets.length; i += 1){
                var input_widget = input_widgets[i]
                if (input_widget.autofocus) {
                    if (input_widget.focus() !== false) {
                        break;
                    }
                }
            }
        }
    });
    /**2016-11-23  开阖静静(gilbert@osbzr.com)
    * pivot 视图改造 (在pivot 视图中 特殊颜色 标示满足条件的字段) 只需要在对应的字段上例如
    *<field name="goods_qty" type="measure" pivot_color="{'color':'blue','greater_than_field':'cost'}"/>
    *<field name="goods_uos_qty" type="measure"   pivot_color="{'color':'blue','greater_than':15 }"/>
    * 表示  name="goods_qty" 或 name="goods_uos_qty" 大于 greater_than常量的 值或者 大于 greater_than_field 显示的值
    * greater_than 为对比的字段  类型为float 是个常量
    * greater_than_field 为字段name 是变量
    * greater_than 和 greater_than_field 同时存在,以greater_than_field为优先
    *2016-12-16  开阖静静(gilbert@osbzr.com)
    *  pivot 视图中某些字段加了权限组字段 但是不起作用 没在相应权限组里面的人任然能够看到对应的字段
    *  bug 造成原因 : 系统原生代码里面没有处理这部分的功能 在取数据的时候取出了所有的可以用来显示的数据
    *  但是系统后台是对前端的数据经过处理的 如果不让看到的字段 会有invisible='1' 和 modifiers:{'invisible':true}
    *  所以可以根据这点差异进行必要数据处理
     *   willstart 有个坑.....willstart 中的代码总会执行..
    * */

    PivotView.include({
         init: function() {
             this.pivot_color = [];
             this.invisible_field = [];
             this._super.apply(this, arguments);
         },
         willStart: function () {
             var self = this;
             self.pivot_color_field = [];

             this.fields_view.arch.children.forEach(function (field) {
                  if(field.attrs.invisible==='1') {
                    self.invisible_field.push(field.attrs.name);
                  }
                 if (field.attrs && field.attrs.pivot_color!=undefined){
                    self.pivot_color_field.push(field.attrs.name);
                    var pivot_color_obj =py.eval(field.attrs.pivot_color);
                    self.pivot_color.push(pivot_color_obj);
                 }
            });
            return this._super.apply(this, arguments);;
        },

        prepare_fields: function (fields) {
            var self = this,
                groupable_types = ['many2one', 'char', 'boolean',
                                   'selection', 'date', 'datetime'];
            this.fields = fields;
            _.each(fields, function (field, name) {
                if ((name !== 'id') && (field.store === true) && self.invisible_field.indexOf(name)<0) {
                    // 不在要隐藏的字段的列表里面
                    if ((field.type === 'integer' || field.type === 'float' || field.type === 'monetary')) {
                        self.measures[name] = field;
                    }
                    if (_.contains(groupable_types, field.type)) {
                        self.groupable_fields[name] = field;
                    }
                }
            });
            this.measures.__count__ = {string: _t("Count"), type: "integer"};
        },
        start: function () {
            var self = this;
             _.each(self.invisible_field, function (field) {
                 if(self.active_measures.indexOf(field)>=0){
                        self.active_measures.splice(self.active_measures.indexOf(field)
                                                   ,self.active_measures.indexOf(field));}
                 if(self.measures[field]!==undefined){delete self.measures[field]}
             });
             this._super.apply(this, arguments);
        },
        change_color: function (rows, $cell, i, j, nbr_measures,field_index) {
            var compare_flag = true,
                greater_than_field = this.pivot_color[field_index].greater_than_field,
                greater_than = this.pivot_color[field_index].greater_than;

            if (!(greater_than_field == "" || greater_than_field == undefined || greater_than_field == null)
                && this.active_measures.indexOf(greater_than_field)>=0){
                compare_flag = rows[i].values[j] > rows[i].values[this.active_measures.indexOf(greater_than_field)
                    + Math.floor(j / nbr_measures) * nbr_measures]
            }else if(!(greater_than == "" || greater_than == undefined || greater_than == null)){
                compare_flag = rows[i].values[j] >greater_than
            }
            if (compare_flag) {
                $cell.css('color', this.pivot_color[field_index].color|| 'black');
            }else if( this.pivot_color[field_index].default_color){
                 $cell.css('color', this.pivot_color[field_index].default_color || 'black');
            }
        },
        draw_rows: function ($tbody, rows) {
            var self = this,
                i, j, value, $row, $cell, $header,
                nbr_measures = this.active_measures.length,
                length = rows[0].values.length,
                display_total = this.main_col.width > 1;

            var groupby_labels = _.map(this.main_row.groupbys, function (gb) {
                return self.fields[gb.split(':')[0]].string;
            });
            var measure_types = this.active_measures.map(function (name) {
                return self.measures[name].type;
            });
            var widgets = this.widgets;
            for (i = 0; i < rows.length; i++) {
                $row = $('<tr>');
                $header = $('<td>')
                    .text(rows[i].title)
                    .data('id', rows[i].id)
                    .css('padding-left', (5 + rows[i].indent * 30) + 'px')
                    .addClass(rows[i].expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed');
                if (rows[i].indent > 0) $header.attr('title', groupby_labels[rows[i].indent - 1]);
                $header.appendTo($row);
                for (j = 0; j < length; j++) {

                    value = formats.format_value(rows[i].values[j], {
                        type: measure_types[j % nbr_measures],
                        widget: widgets[j % nbr_measures]
                    });
                    $cell = $('<td>')
                        .data('id', rows[i].id)
                        .data('col_id', rows[i].col_ids[Math.floor(j / nbr_measures)])
                        .toggleClass('o_empty', !value)
                        .text(value)
                        .addClass('o_pivot_cell_value text-right');
                    if (((j >= length - this.active_measures.length) && display_total) || i === 0) {
                        $cell.css('font-weight', 'bold');
                    }
                    if(this.pivot_color_field.indexOf(this.active_measures[j % nbr_measures])>=0){
                        var field_index = this.pivot_color_field.indexOf(this.active_measures[j % nbr_measures])
                        this.change_color(rows, $cell, i, j, nbr_measures,field_index);
                    }
                    $row.append($cell);
                    $cell.toggleClass('hidden-xs', j < length - this.active_measures.length);
                }
                $tbody.append($row);
            }
        },

    });
    /** 2016-11-30  开阖静静(gilbert@osbzr.com)
    *
    * 鼠标悬停即展开(二级菜单) --前提是在backend_theme
     * 主题下| 在没有安装主题的场景下并没有测试  */
    Menu.include({
        events: {
            mouseenter: "on_open_second_menu",
            mouseleave: "on_close_second_menu",
        },

        on_open_second_menu: function (e) {
            var $target = $(e.target);
            $target.parent().addClass('open');
            if($target.attr('aria-expanded')!=undefined && !$target.attr('aria-expanded')){
                $target.attr('aria-expanded', true);
            };
            var menu_list = $target.parent().find('.oe_secondary_submenu');
            menu_list.show();
        },
        on_close_second_menu: function (e) {
            var $target = $(e.target);
            $target.parent().removeClass('open');
            var menu_list = $target.parent().find('.oe_secondary_submenu');
            var dropdown_a = $target.parent().find('.dropdown-toggle')
            if(dropdown_a.attr('aria-expanded')!=undefined && $target.attr('aria-expanded')){
                $target.attr('aria-expanded', false);
            };
            menu_list.hide();
        },
        bind_menu: function () {
            this._super.apply(this, arguments);
            this.$second_menu = this.$el.parents().find('.dropdown-toggle');
            this.$second_menu.on('mouseenter', this.on_open_second_menu.bind(this));
            this.$second_menu.parent().on('mouseleave', this.on_close_second_menu.bind(this));
        }

    });
    /************************************************************
     *2017-01-10  开阖静静(gilbert@osbzr.com)
     * 实现在form页面上的 one2many字段 子字段的必输报错的详细提示
     *对js基础方法的理解不是很深刻,难免用的不是很恰当,有比较好的实现再去修改,
     * ***********************************************************/
    FormView.include({
         on_invalid: function() {
            var warnings = _(this.fields).chain()
                .filter(function (f) {return !f.is_valid(); })
                .map(function (f) {
                    var  field_list = ''
                    if((f.field.type=='one2many' || f.field.type=='many2many')){
                        var list =_.map(f.views[0].fields_view.fields,function (value, key_vals) {
                            if(value.required ||(value.__attrs && value.__attrs.required==='1')){
                                return [key_vals,value.string]}
                        }).filter(function(value){if(value){return value}});
                        var dict_list = f.dataset.cache;
                        var list_keys = _.map(list,function (value) {return value[0]});
                        var list_vals = _.map(list,function (value) {return value[1]});
                        if(dict_list) {
                            var index = 0;
                            var break_flag = false;
                            field_list = _.map(dict_list, function (value,key) {
                                index= index+1;
                                if(break_flag){return undefined}else{
                                    var field_list_message = _.map(value.values,function (field,field_name) {
                                        if(field===false &&list_keys.indexOf(field_name)>=0){return field_name
                                     }else{return undefined}}).filter(function(value){return value});
                                    if(_.any(field_list_message)){
                                     break_flag = true
                                     return "   <li>第"+(index)+"行 "+ list_vals[list_keys.indexOf(field_list_message[0])]+"</li>";}
                                }
                           }).filter(function(value){if(value){return value}});
                        }
                    }
                    return _.str.sprintf('<li>%s</li>',_.escape(f.string)+field_list);
                }).value();
            warnings.unshift('<ul>');
            warnings.push('</ul>');
            this.do_warn(_t("The following fields are invalid:"), warnings.join(''));
         },
    });

var BetterList = Widget.extend({
    template: 'BetterList',

    init: function(parent, data){
        return this._super.apply(this, arguments);
    },

    start: function(){
        return true;
    },
});
core.action_registry.add('better.list', BetterList);

// realize pdf view function.
var FieldPdfViewer = FieldBinaryFile.extend({
    template: 'FieldPdfViewer',
    init: function(){
        this._super.apply(this, arguments);
        this.PDFViewerApplication = false;
    },
    get_uri: function(){
        var query_obj = {
            model: this.view.dataset.model,
            field: this.name,
            id: this.view.datarecord.id
        };
        var query_string = $.param(query_obj);
        var url = encodeURIComponent('/web/image?' + query_string);
        var viewer_url = '/web/static/lib/pdfjs/web/viewer.html?file=';
        return viewer_url + url;
    },
    on_file_change: function(ev) {
        this._super.apply(this, arguments);
        if(this.PDFViewerApplication){
            var files = ev.target.files;
            if (!files || files.length === 0) {
              return;
            }
            var file = files[0];
            // TOCheck: is there requirement to fallback on FileReader if browser don't support URL
            this.PDFViewerApplication.open(URL.createObjectURL(file), 0);
        }
    },
    render_value: function() {
        var $pdf_viewer = this.$('.o_form_pdf_controls').children().add(this.$('.o_pdfview_iframe')),
            $select_upload_el = this.$('.o_select_file_button').first(),
            $iFrame = this.$('.o_pdfview_iframe'),
            value = this.get('value'),
            self = this;

        var bin_size = utils.is_bin_size(value);
        $iFrame.on('load', function(){
            self.PDFViewerApplication = this.contentWindow.window.PDFViewerApplication;
            self.disable_buttons(this);
        });
        if (this.get("effective_readonly")) {
            if (value) {
                this.$el.off('click'); // off click event(on_save_as) of FieldBinaryFile
                $iFrame.attr('src', this.get_uri());
            }
        } else {
            if (value) {
                $pdf_viewer.removeClass('o_hidden');
                $select_upload_el.addClass('o_hidden');
                if(bin_size){
                    $iFrame.attr('src', this.get_uri());
                }
            } else {
                $pdf_viewer.addClass('o_hidden');
                $select_upload_el.removeClass('o_hidden');
            }
        }
    },
    disable_buttons: function(iframe){
        if (this.get("effective_readonly")){
            $(iframe).contents().find('button#download').hide();
        }
        $(iframe).contents().find('button#openFile').hide();
    }

});
core.form_widget_registry.add('pdf_viewer', FieldPdfViewer);
});