odoo widgets.js 笔记
阅读原文时间:2023年07月12日阅读:1

// 在OpenERP的Web框架内,
// 通过声明一个函数来声明一个JavaScript模块【openerp.ext_picking就是这个JS模块】,
// 并把这个函数放在全局变量openerp的属性内.
// 这个属性名称必须和OpenERP addon 模块名称一致【文件夹就是ext_picking】
// 【采用匿名函数封装,函数openerp_ext_picking_widgets通过后面的openerp_ext_picking_widgets(openerp)调用】
function openerp_ext_picking_widgets(instance) {

var extmodule = instance.ext\_stock;  
var module = instance.picking;  
var \_t = instance.web.\_t;  
var QWeb = instance.web.qweb;

// This widget makes sure that the scaling is disabled on mobile devices.  
// Widgets that want to display fullscreen on mobile phone need to extend this  
// widget.  
// MobileWidget 如果在手机端显示,需要扩展该部件  
// start()在部件初始化后,被自动调用。它接受指令去显示其内容。用它向用户显示一些内容。  
// 要做到这一点,我们使用所有部件都有的 $el 属性。该属性是一个 jQuery对象, 表示部件对应的 HTML 标签的根标签。  
// 部件包含了多个 HTML 标签,这些 HTM 标签有一个统一的根标签。  
// 默认情况下,部件都有一个空的根标签:一个<div>  
// $('#oe-mobilewidget-viewport'):选择指定的id的HTML元素  
// $('head'):选择特定的HTML元素, .append():追加内容到后面  
// 销毁部件 destroy()  
extmodule.MobileWidget = instance.web.Widget.extend({  
    start: function () {  
        if (!$('#oe-mobilewidget-viewport').length) {  
            $('head').append('<meta id="oe-mobilewidget-viewport" name="viewport" content="initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">');  
        }  
        return this.\_super();  
    },

    destroy: function () {  
        $('#oe-mobilewidget-viewport').remove();  
        return this.\_super();  
    },  
});

// 操作界面  
// 在部件中重载init()时,必须以父部件作为第一参数传入,并调用传入给this.\_super()  
// 重载方法,使用 this.\_super()调用原来的方法  
// get\_header: 调用父部件下的 get\_header() 方法  
extmodule.PickingEditorWidget = instance.web.Widget.extend({  
    template: 'PickingEditorWidget',  
    init: function (parent, options) {  
        this.\_super(parent, options);  
        var self = this;  
        this.rows = \[\];  
        this.search\_filter = "";  
        // 伪类选择器jQuery.expr\[":"\]  
        jQuery.expr\[":"\].Contains = jQuery.expr.createPseudo(function (arg) {  
            return function (elem) {  
                return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;  
            };  
        });  
    },  
    get\_header: function () {  
        return this.getParent().get\_header();  
    },  
    get\_location: function () {  
        var model = this.getParent();  
        var locations = \[\];  
        var self = this;  
        \_.each(model.locations, function (loc) {  
            locations.push({name: loc.complete\_name, id: loc.id,});  
        });

        return locations;  
    },  
    get\_logisticunit: function () {  
        var model = this.getParent();  
        var ul = \[\];  
        var self = this;  
        \_.each(model.uls, function (ulog) {  
            ul.push({name: ulog.name, id: ulog.id,});  
        });  
        return ul;  
    },  
    get\_rows: function () {  
        var model = this.getParent();  
        this.rows = \[\];  
        var self = this;  
        var pack\_created = \[\];  
        \_.each(model.packoplines, function (packopline) {  
            var pack = undefined;  
            var color = "";  
            if (packopline.product\_id\[1\] !== undefined) {  
                pack = packopline.package\_id\[1\];  
            }  
            if (packopline.product\_qty == packopline.qty\_done) {  
                color = "success ";  
            }  
            if (packopline.product\_qty < packopline.qty\_done) {  
                color = "danger ";  
            }  
            //also check that we don't have a line already existing for that package  
            if (packopline.result\_package\_id\[1\] !== undefined && $.inArray(packopline.result\_package\_id\[0\], pack\_created) === -1) {  
                var myPackage = $.grep(model.packages, function (e) {  
                    return e.id == packopline.result\_package\_id\[0\];  
                })\[0\];  
                self.rows.push({  
                    cols: {  
                        product: packopline.result\_package\_id\[1\],  
                        qty: '',  
                        rem: '',  
                        uom: undefined,  
                        lot: undefined,  
                        pack: undefined,  
                        container: packopline.result\_package\_id\[1\],  
                        container\_id: undefined,  
                        loc: packopline.location\_id\[1\],  
                        dest: packopline.location\_dest\_id\[1\],  
                        id: packopline.result\_package\_id\[0\],  
                        product\_id: undefined,  
                        can\_scan: false,  
                        head\_container: true,  
                        processed: packopline.processed,  
                        package\_id: myPackage.id,  
                        ul\_id: myPackage.ul\_id\[0\],  
                        display\_name: 'select'+packopline.result\_package\_id\[0\],  
                    },  
                    classes: ('success container\_head ') + (packopline.processed === "true" ? 'processed hidden ' : ''),  
                });  
                pack\_created.push(packopline.result\_package\_id\[0\]);  
            }  
            self.rows.push({  
                cols: {  
                    product: packopline.product\_id\[1\] || packopline.package\_id\[1\],  
                    qty: packopline.product\_qty,  
                    rem: packopline.qty\_done,  
                    uom: packopline.product\_uom\_id\[1\],  
                    lot: packopline.lot\_id\[1\],  
                    pack: pack,  
                    container: packopline.result\_package\_id\[1\],  
                    container\_id: packopline.result\_package\_id\[0\],  
                    loc: packopline.location\_id\[1\],  
                    dest: packopline.location\_dest\_id\[1\],  
                    id: packopline.id,  
                    product\_id: packopline.product\_id\[0\],  
                    can\_scan: packopline.result\_package\_id\[1\] === undefined ? true : false,  
                    head\_container: false,  
                    processed: packopline.processed,  
                    package\_id: undefined,  
                    ul\_id: -1,  
                    display\_name: 'select'+packopline.id,  
                },  
                classes: color + (packopline.result\_package\_id\[1\] !== undefined ? 'in\_container\_hidden ' : '') + (packopline.processed === "true" ? 'processed hidden ' : ''),  
            });  
        });  
        //sort element by things to do, then things done, then grouped by packages  
        group\_by\_container = \_.groupBy(self.rows, function (row) {  
            return row.cols.container;  
        });  
        var sorted\_row = \[\];  
        if (group\_by\_container.undefined !== undefined) {  
            group\_by\_container.undefined.sort(function (a, b) {  
                return (b.classes === '') - (a.classes === '');  
            });  
            $.each(group\_by\_container.undefined, function (key, value) {  
                sorted\_row.push(value);  
            });  
        }

        $.each(group\_by\_container, function (key, value) {  
            if (key !== 'undefined') {  
                $.each(value, function (k, v) {  
                    sorted\_row.push(v);  
                });  
            }  
        });

        return sorted\_row;  
    },  
    // .text:设置元素内的内容  
    renderElement: function () {  
        var self = this;  
        this.\_super();  
        this.check\_content\_screen();

        this.$('.js\_pick\_done').click(function () {  
            self.getParent().done();  
        });  
        this.$('.js\_pick\_print').click(function () {  
            self.getParent().print\_picking();  
        });  
        this.$('.oe\_pick\_app\_header').text(self.get\_header());  
        this.$('.oe\_searchbox').keyup(function (event) {  
            self.on\_searchbox($(this).val());  
        });  
        this.$('.js\_putinpack').click(function () {  
            self.getParent().pack();  
        });  
        this.$('.js\_drop\_down').click(function () {  
            self.getParent().drop\_down();  
        });  
        this.$('.js\_clear\_search').click(function () {  
            self.on\_searchbox('');  
            self.$('.oe\_searchbox').val('');  
        });  
        this.$('.oe\_searchbox').focus(function () {  
            self.getParent().barcode\_scanner.disconnect();  
        });  
        this.$('.oe\_searchbox').blur(function () {  
            self.getParent().barcode\_scanner.connect(function (ean) {  
                self.getParent().scan(ean);  
            });  
        });  
        // #js\_select关闭扫码  
        this.$('#js\_select').focus(function () {  
            self.getParent().barcode\_scanner.disconnect();  
        });  
        this.$('#js\_select').blur(function () {  
            self.getParent().barcode\_scanner.connect(function (ean) {  
                self.getParent().scan(ean);  
            });  
        });  
        this.$('#js\_select').change(function () {  
            var selection = self.$('#js\_select option:selected').attr('value');  
            if (selection === "ToDo") {  
                self.getParent().$('.js\_pick\_pack').removeClass('hidden');  
                self.getParent().$('.js\_drop\_down').removeClass('hidden');  
                self.$('.js\_pack\_op\_line.processed').addClass('hidden');  
                self.$('.js\_pack\_op\_line:not(.processed)').removeClass('hidden')  
            }  
            else {  
                self.getParent().$('.js\_pick\_pack').addClass('hidden');  
                self.getParent().$('.js\_drop\_down').addClass('hidden');  
                self.$('.js\_pack\_op\_line.processed').removeClass('hidden');  
                self.$('.js\_pack\_op\_line:not(.processed)').addClass('hidden')  
            }  
            self.on\_searchbox(self.search\_filter);  
        });  
        this.$('.js\_plus').click(function () {  
            var id = $(this).data('product-id');  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');  
            self.getParent().scan\_product\_id(id, true, op\_id);  
        });  
        this.$('.js\_minus').click(function () {  
            var id = $(this).data('product-id');  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');  
            self.getParent().scan\_product\_id(id, false, op\_id);  
        });  
        this.$('.js\_unfold').click(function () {  
            var op\_id = $(this).parent().data('id');  
            var line = $(this).parent();  
            //select all js\_pack\_op\_line with class in\_container\_hidden and correct container-id  
            select = self.$('.js\_pack\_op\_line.in\_container\_hidden\[data-container-id=' + op\_id + '\]')  
            if (select.length > 0) {  
                //we unfold  
                line.addClass('warning');  
                select.removeClass('in\_container\_hidden');  
                select.addClass('in\_container');  
            }  
            else {  
                //we fold  
                line.removeClass('warning');  
                select = self.$('.js\_pack\_op\_line.in\_container\[data-container-id=' + op\_id + '\]')  
                select.removeClass('in\_container');  
                select.addClass('in\_container\_hidden');  
            }  
        });  
        this.$('.js\_create\_lot').click(function () {  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');  
            var lot\_name = false;  
            self.$('.js\_lot\_scan').val('');  
            var $lot\_modal = self.$el.siblings('#js\_LotChooseModal');  
            //disconnect scanner to prevent scanning a product in the back while dialog is open  
            self.getParent().barcode\_scanner.disconnect();  
            $lot\_modal.modal();  
            //focus input  
            $lot\_modal.on('shown.bs.modal', function () {  
                self.$('.js\_lot\_scan').focus();  
            });  
            //reactivate scanner when dialog close 关闭对话框激活扫描界面  
            $lot\_modal.on('hidden.bs.modal', function () {  
                self.getParent().barcode\_scanner.connect(function (ean) {  
                    self.getParent().scan(ean);  
                });  
            });  
            self.$('.js\_lot\_scan').focus();  
            //button action  
            self.$('.js\_validate\_lot').click(function () {  
                //get content of input  
                var name = self.$('.js\_lot\_scan').val();  
                if (name.length !== 0) {  
                    lot\_name = name;  
                }  
                $lot\_modal.modal('hide');  
                //we need this here since it is not sure the hide event  
                //will be catch because we refresh the view after the create\_lot call  
                self.getParent().barcode\_scanner.connect(function (ean) {  
                    self.getParent().scan(ean);  
                });  
                self.getParent().create\_lot(op\_id, lot\_name);  
            });  
        });  
        this.$('.js\_delete\_pack').click(function () {  
            var pack\_id = $(this).parents("\[data-id\]:first").data('id');  
            self.getParent().delete\_package\_op(pack\_id);  
        });  
        this.$('.js\_print\_pack').click(function () {  
            var pack\_id = $(this).parents("\[data-id\]:first").data('id');  
            // $(this).parents("\[data-id\]:first").data('id')  
            self.getParent().print\_package(pack\_id);  
        });  
        this.$('.js\_submit\_value').submit(function (event) {  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');  
            var value = parseFloat($("input", this).val());  
            if (value >= 0) {  
                self.getParent().set\_operation\_quantity(value, op\_id);  
            }  
            $("input", this).val("");  
            return false;  
        });  
        this.$('.js\_qty').focus(function () {  
            self.getParent().barcode\_scanner.disconnect();  
        });  
        this.$('.js\_qty').blur(function () {  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');  
            var value = parseFloat($(this).val());  
            if (value >= 0) {  
                self.getParent().set\_operation\_quantity(value, op\_id);  
            }

            self.getParent().barcode\_scanner.connect(function (ean) {  
                self.getParent().scan(ean);  
            });  
        });  
        this.$('.js\_change\_src').click(function () {  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');//data('op\_id');  
            self.$('#js\_loc\_select').addClass('source');  
            self.$('#js\_loc\_select').data('op-id', op\_id);  
            self.$el.siblings('#js\_LocationChooseModal').modal();  
        });  
        this.$('.js\_change\_dst').click(function () {  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');  
            self.$('#js\_loc\_select').data('op-id', op\_id);  
            self.$el.siblings('#js\_LocationChooseModal').modal();  
        });  
        this.$('.js\_pack\_change\_dst').click(function () {  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');  
            self.$('#js\_loc\_select').addClass('pack');  
            self.$('#js\_loc\_select').data('op-id', op\_id);  
            self.$el.siblings('#js\_LocationChooseModal').modal();  
        });

        this.$('.js\_validate\_location').click(function () {  
            //get current selection  
            var select\_dom\_element = self.$('#js\_loc\_select');  
            var loc\_id = self.$('#js\_loc\_select option:selected').data('loc-id');  
            var src\_dst = false;  
            var op\_id = select\_dom\_element.data('op-id');

            if (select\_dom\_element.hasClass('pack')) {  
                select\_dom\_element.removeClass('source');  
                op\_ids = \[\];  
                self.$('.js\_pack\_op\_line\[data-container-id=' + op\_id + '\]').each(function () {  
                    op\_ids.push($(this).data('id'));  
                });  
                op\_id = op\_ids;  
            }  
            else if (select\_dom\_element.hasClass('source')) {  
                src\_dst = true;  
                select\_dom\_element.removeClass('source');  
            }  
            if (loc\_id === false) {  
                //close window  
                self.$el.siblings('#js\_LocationChooseModal').modal('hide');  
            }  
            else {  
                self.$el.siblings('#js\_LocationChooseModal').modal('hide');  
                self.getParent().change\_location(op\_id, parseInt(loc\_id), src\_dst);

            }  
        });  
        // 扫描产品  
        this.$('.js\_clear\_search2').click(function () {  
            self.on\_searchbox2('');  
            self.$('.oe\_searchbox2').val('');  
        });  
        this.$('#oe\_searchbox2').keyup(function (event) {  
            self.getParent().scan($(this).val());  
            self.on\_searchbox2($(this).val());  
        });  
        this.$('#oe\_searchbox2').focus(function () {  
            self.getParent().barcode\_scanner.disconnect();  
        });  
        this.$('#oe\_searchbox2').blur(function () {  
            self.$('#oe\_searchbox2').val('');  
            self.getParent().barcode\_scanner.connect(function (ean) {  
                self.getParent().scan(ean);  
            });  
        });

        // 更改目标位置  
        this.$('.js\_select\_class').blur(function () {  
            self.getParent().barcode\_scanner.connect(function (ean) {  
                self.getParent().scan(ean);  
            });  
        });  
        this.$('.js\_select\_class').focus(function () {  
            // self.getParent().barcode\_scanner.disconnect();  
        });  
        this.$('.js\_select\_class').change(function () {  
            //get current selection  
            var op\_id = $(this).parents("\[data-id\]:first").data('id');

            $(this).parents("\[data-id\]:first").css('background-color', 'grey');

            var select\_str = 'select' + op\_id;  
            var select\_dom\_element = self.$('#' + select\_str);  
            var loc\_id = self.$('#'+ select\_str + ' option:selected').data('loc-id');  
            var src\_dst = false;  
            self.$('#'+select\_str).data('op-id', op\_id);

            if (select\_dom\_element.hasClass('pack')) {  
                select\_dom\_element.removeClass('source');  
                op\_ids = \[\];  
                self.$('.js\_pack\_op\_line\[data-container-id=' + op\_id + '\]').each(function () {  
                    op\_ids.push($(this).data('id'));  
                });  
                op\_id = op\_ids;  
            }  
            else if (select\_dom\_element.hasClass('source')) {  
                src\_dst = true;  
                select\_dom\_element.removeClass('source');  
            }  
            if (loc\_id === false) {  
                //close window  
                self.$el.siblings('#js\_LocationChooseModal').modal('hide');  
            }  
            else {  
                self.$el.siblings('#js\_LocationChooseModal').modal('hide');  
                self.getParent().change\_location(op\_id, parseInt(loc\_id), src\_dst);  
            }

        });  
        // this.$('.js\_LocationChooseModal2').on('change', function (){  
        //     return $(this).attr('id');  
        // });  
        this.$('.js\_pack\_configure').click(function () {  
            var pack\_id = $(this).parents(".js\_pack\_op\_line:first").data('package-id');  
            var ul\_id = $(this).parents(".js\_pack\_op\_line:first").data('ulid');  
            self.$('#js\_packconf\_select').val(ul\_id);  
            self.$('#js\_packconf\_select').data('pack-id', pack\_id);  
            self.$el.siblings('#js\_PackConfModal').modal();  
        });  
        this.$('.js\_validate\_pack').click(function () {  
            //get current selection  
            var select\_dom\_element = self.$('#js\_packconf\_select');  
            var ul\_id = self.$('#js\_packconf\_select option:selected').data('ul-id');  
            var pack\_id = select\_dom\_element.data('pack-id');  
            self.$el.siblings('#js\_PackConfModal').modal('hide');  
            if (pack\_id) {  
                self.getParent().set\_package\_pack(pack\_id, ul\_id);  
                $('.container\_head\[data-package-id="' + pack\_id + '"\]').data('ulid', ul\_id);  
            }  
        });

        //remove navigtion bar from default openerp GUI  
        $('td.navbar').html('<div></div>');  
    },  
    on\_searchbox: function (query) {  
        //hide line that has no location matching the query and highlight location that match the query  
        this.search\_filter = query;  
        var processed = ".processed";  
        if (this.$('#js\_select option:selected').attr('value') == "ToDo") {  
            processed = ":not(.processed)";  
        }  
        if (query !== '') {  
            this.$('.js\_loc:not(.js\_loc:Contains(' + query + '))').removeClass('info');  
            this.$('.js\_loc:Contains(' + query + ')').addClass('info');  
            this.$('.js\_pack\_op\_line' + processed + ':not(.js\_pack\_op\_line:has(.js\_loc:Contains(' + query + ')))').addClass('hidden');  
            this.$('.js\_pack\_op\_line' + processed + ':has(.js\_loc:Contains(' + query + '))').removeClass('hidden');  
        }  
        //if no query specified, then show everything  
        if (query === '') {  
            this.$('.js\_loc').removeClass('info');  
            this.$('.js\_pack\_op\_line' + processed + '.hidden').removeClass('hidden');  
        }  
        this.check\_content\_screen();  
    },

    on\_searchbox2: function (query) {

        this.check\_content\_screen();  
    },

    check\_content\_screen: function () {  
        //get all visible element and if none has positive qty, disable put in pack and process button  
        var self = this;  
        var processed = this.$('.js\_pack\_op\_line.processed');  
        var qties = this.$('.js\_pack\_op\_line:not(.processed):not(.hidden) .js\_qty').map(function () {  
            return $(this).val()  
        });  
        var container = this.$('.js\_pack\_op\_line.container\_head:not(.processed):not(.hidden)')  
        var disabled = true;  
        $.each(qties, function (index, value) {  
            if (parseInt(value) > 0) {  
                disabled = false;  
            }  
        });

        if (disabled) {  
            if (container.length === 0) {  
                self.$('.js\_drop\_down').addClass('disabled');  
            }  
            else {  
                self.$('.js\_drop\_down').removeClass('disabled');  
            }  
            self.$('.js\_pick\_pack').addClass('disabled');  
            if (processed.length === 0) {  
                self.$('.js\_pick\_done').addClass('disabled');  
            }  
            else {  
                self.$('.js\_pick\_done').removeClass('disabled');  
            }  
        }  
        else {  
            self.$('.js\_drop\_down').removeClass('disabled');  
            self.$('.js\_pick\_pack').removeClass('disabled');  
            self.$('.js\_pick\_done').removeClass('disabled');  
        }  
    },  
    get\_current\_op\_selection: function (ignore\_container) {  
        //get ids of visible on the screen  
        pack\_op\_ids = \[\]  
        this.$('.js\_pack\_op\_line:not(.processed):not(.js\_pack\_op\_line.hidden):not(.container\_head)').each(function () {  
            cur\_id = $(this).data('id');  
            pack\_op\_ids.push(parseInt(cur\_id));  
        });  
        //get list of element in this.rows where rem > 0 and container is empty is specified  
        list = \[\]  
        \_.each(this.rows, function (row) {  
            if (row.cols.rem > 0 && (ignore\_container || row.cols.container === undefined)) {  
                list.push(row.cols.id);  
            }  
        });  
        //return only those visible with rem qty > 0 and container empty  
        return \_.intersection(pack\_op\_ids, list);  
    },  
    remove\_blink: function () {  
        this.$('.js\_pack\_op\_line.blink\_me').removeClass('blink\_me');  
    },  
    // ----------------修改blink 扫描产品刷新页面  
    blink: function (op\_id) {  
        this.$('.js\_pack\_op\_line\[data-id="' + op\_id + '"\]').addClass('blink\_me');// 在数量发生改变的行增加类,产生闪烁效果  
    },  
    check\_done: function () {  
        var model = this.getParent();  
        var self = this;  
        var done = true;  
        \_.each(model.packoplines, function (packopline) {  
            if (packopline.processed === "false") {  
                done = false;  
                return done;  
            }  
        });  
        return done;  
    },  
    get\_visible\_ids: function () {  
        var self = this;  
        var visible\_op\_ids = \[\]  
        var op\_ids = this.$('.js\_pack\_op\_line:not(.processed):not(.hidden):not(.container\_head):not(.in\_container):not(.in\_container\_hidden)').map(function () {  
            return $(this).data('id');  
        });  
        $.each(op\_ids, function (key, op\_id) {  
            visible\_op\_ids.push(parseInt(op\_id));  
        });  
        return visible\_op\_ids;  
    },  
});

// PickingMenuWidget 继承自 MobileWidget  
// bind() 方法为被选元素添加一个或多个事件处理程序,并规定事件发生时运行的函数。  
extmodule.PickingMenuWidget = extmodule.MobileWidget.extend({  
    template: 'PickingMenuWidget',  
    init: function (parent, params) {  
        this.\_super(parent, params);  
        var self = this;  
        $(window).bind('hashchange', function () {  
            var states = $.bbq.getState();  
            if (states.action === "stock.ui") {  
                self.do\_action({  
                    type: 'ir.actions.client',  
                    tag: 'stock.ui',  
                    target: 'current',  
                }, {  
                    clear\_breadcrumbs: true,  
                });  
            }  
        });  
        this.picking\_types = \[\];  
        this.loaded = this.load();  
        this.scanning\_type = 0;  
        // 实例化部件BarcodeScanner  
        this.barcode\_scanner = new extmodule.BarcodeScanner();  
        this.pickings\_by\_type = {};  
        this.pickings\_by\_id = {};  
        this.picking\_search\_string = "";  
    },  
    load: function () {  
        var self = this;  
        return new instance.web.Model('stock.picking.type').get\_func('search\_read')(\[\], \[\])  
            .then(function (types) {  
                self.picking\_types = types;  
                type\_ids = \[\];  
                for (var i = 0; i < types.length; i++) {  
                    self.pickings\_by\_type\[types\[i\].id\] = \[\];  
                    type\_ids.push(types\[i\].id);  
                }  
                self.pickings\_by\_type\[0\] = \[\];

                return new instance.web.Model('stock.picking').call('search\_read', \[\[\['state', 'in', \['assigned', 'partially\_available'\]\], \['picking\_type\_id', 'in', type\_ids\]\], \[\]\], {context: new instance.web.CompoundContext()});

            }).then(function (pickings) {  
                self.pickings = pickings;  
                for (var i = 0; i < pickings.length; i++) {  
                    var picking = pickings\[i\];  
                    self.pickings\_by\_type\[picking.picking\_type\_id\[0\]\].push(picking);  
                    self.pickings\_by\_id\[picking.id\] = picking;  
                    self.picking\_search\_string += '' + picking.id + ':' + (picking.name ? picking.name.toUpperCase() : '') + '\\n';  
                }

            });  
    },  
    renderElement: function () {  
        this.\_super();  
        var self = this;  
        this.$('.js\_pick\_quit').click(function () {  
            self.quit();  
        });  
        this.$('.js\_pick\_scan').click(function () {  
            self.scan\_picking($(this).data('id'));  
        });  
        this.$('.js\_pick\_last').click(function () {  
            self.goto\_last\_picking\_of\_type($(this).data('id'));  
        });  
        this.$('.oe\_searchbox').keyup(function (event) {  
            self.on\_searchbox($(this).val());  
        });  
        //remove navigtion bar from default openerp GUI  
        $('td.navbar').html('<div></div>');  
    },  
    start: function () {  
        this.\_super();  
        var self = this;  
        instance.webclient.set\_content\_full\_screen(true);  
        this.barcode\_scanner.connect(function (barcode) {  
            self.on\_scan(barcode);  
        });  
        this.loaded.then(function () {  
            self.renderElement();  
        });  
    },  
    goto\_picking: function (picking\_id) {  
        $.bbq.pushState('#action=stock.ui&picking\_id=' + picking\_id);  
        $(window).trigger('hashchange');  
    },  
    goto\_last\_picking\_of\_type: function (type\_id) {  
        $.bbq.pushState('#action=stock.ui&picking\_type\_id=' + type\_id);  
        $(window).trigger('hashchange');  
    },  
    search\_picking: function (barcode) {  
        try {  
            var re = RegExp("(\[0-9\]+):.\*?" + barcode.toUpperCase(), "gi");  
        }  
        catch (e) {  
            //avoid crash if a not supported char is given (like '\\' or ')')  
            return \[\];  
        }

        var results = \[\];  
        for (var i = 0; i < 100; i++) {  
            r = re.exec(this.picking\_search\_string);  
            if (r) {  
                var picking = this.pickings\_by\_id\[Number(r\[1\])\];  
                if (picking) {  
                    results.push(picking);  
                }  
            } else {  
                break;  
            }  
        }  
        return results;  
    },  
    on\_scan: function (barcode) {  
        var self = this;  
        for (var i = 0, len = this.pickings.length; i < len; i++) {  
            var picking = this.pickings\[i\];  
            if (picking.name.toUpperCase() === $.trim(barcode.toUpperCase())) {  
                this.goto\_picking(picking.id);  
                break;  
            }  
        }  
        this.$('.js\_picking\_not\_found').removeClass('hidden');

        clearTimeout(this.picking\_not\_found\_timeout);  
        this.picking\_not\_found\_timeout = setTimeout(function () {  
            self.$('.js\_picking\_not\_found').addClass('hidden');  
        }, 2000);

    },  
    on\_searchbox: function (query) {  
        var self = this;  
        clearTimeout(this.searchbox\_timeout);  
        this.searchbox\_timout = setTimeout(function () {  
            if (query) {  
                self.$('.js\_picking\_not\_found').addClass('hidden');  
                self.$('.js\_picking\_categories').addClass('hidden');  
                self.$('.js\_picking\_search\_results').html(  
                    QWeb.render('PickingSearchResults', {results: self.search\_picking(query)})  
                );  
                self.$('.js\_picking\_search\_results .oe\_picking').click(function () {  
                    self.goto\_picking($(this).data('id'));  
                });  
                self.$('.js\_picking\_search\_results').removeClass('hidden');  
            } else {  
                self.$('.js\_title\_label').removeClass('hidden');  
                self.$('.js\_picking\_categories').removeClass('hidden');  
                self.$('.js\_picking\_search\_results').addClass('hidden');  
            }  
        }, 100);  
    },  
    quit: function () {  
        return new instance.web.Model("ir.model.data").get\_func("search\_read")(\[\['name', '=', 'action\_picking\_type\_form'\]\], \['res\_id'\]).pipe(function (res) {  
            window.location = '/web#action=' + res\[0\]\['res\_id'\];  
        });  
    },  
    destroy: function () {  
        this.\_super();  
        this.barcode\_scanner.disconnect();  
        instance.webclient.set\_content\_full\_screen(false);  
    },  
});  
// 把部件注册为客户端的action。当我们点击菜单项时,客户端action让部件显示出来  
openerp.web.client\_actions.add('stock.menu', 'instance.ext\_stock.PickingMenuWidget');

// PickingMainWidget 继承自 MobileWidget  
// 'hashchange': 监听浏览器url变化?  
// bbq: 返回按钮和查询库  
// $.bbq.getState():返回浏览器地址栏参数和值  
extmodule.PickingMainWidget = extmodule.MobileWidget.extend({  
    template: 'PickingMainWidget',  
    init: function (parent, params) {  
        this.\_super(parent, params);  
        var self = this;

        $(window).bind('hashchange', function () {  
            var states = $.bbq.getState();  
            if (states.action === "stock.menu") {  
                self.do\_action({  
                    type: 'ir.actions.client',  
                    tag: 'stock.menu',  
                    target: 'current',  
                }, {  
                    clear\_breadcrumbs: true,  
                });  
            }  
        });  
        init\_hash = $.bbq.getState();  
        this.picking\_type\_id = init\_hash.picking\_type\_id ? init\_hash.picking\_type\_id : 0;  
        this.picking\_id = init\_hash.picking\_id ? init\_hash.picking\_id : undefined;  
        this.picking = null;  
        this.pickings = \[\];  
        this.packoplines = null;  
        this.selected\_operation = {id: null, picking\_id: null};  
        this.packages = null;  
        this.barcode\_scanner = new extmodule.BarcodeScanner();  
        this.locations = \[\];  
        this.uls = \[\];  
        if (this.picking\_id) {  
            this.loaded = this.load(this.picking\_id);  
        } else {  
            this.loaded = this.load();  
        }  
        // -----------------------------  
        this.select\_id = null;  
        this.barcode\_scan\_bool = false;

    },

    // load the picking data from the server. If picking\_id is undefined, it will take the first picking  
    // belonging to the category  
    // instance.web.Model('stock.picking'):js创建类(数据表)实例  
    // .call 调用类(数据表)实例的方法,返回deferred  
    // .then(): 异步执行, then()前的方法执行完后再执行then()内部的程序,then()有两个参数,那么第一个参数是done()方法的回调函数,第二个参数是fail()方法的回调方法  
    // CompoundContext这个类用来传递用户上下文(语言,时区等)给服务器  
    // deferred 是jQuery的回调函数解决方案  
    // .resolve():手动改变deferred对象的运行状态为"已完成",从而立即触发done()方法  
    // .reject()将deferred对象的运行状态变为"已失败",从而立即触发fail()方法  
    load: function (picking\_id) {  
        var self = this;

        function load\_picking\_list(type\_id) {  
            var pickings = new $.Deferred();  
            new instance.web.Model('stock.picking')  
                .call('get\_next\_picking\_for\_ui', \[{'default\_picking\_type\_id': parseInt(type\_id)}\])  
                .then(function (picking\_ids) {  
                    if (!picking\_ids || picking\_ids.length === 0) {  
                        (new instance.web.Dialog(self, {  
                            title: \_t('No Picking Available'),  
                            buttons: \[{  
                                text: \_t('Ok'),  
                                click: function () {  
                                    self.menu();  
                                }  
                            }\]  
                        }, \_t('<p>We could not find a picking to display.</p>'))).open();

                        pickings.reject();  
                    } else {  
                        self.pickings = picking\_ids;  
                        pickings.resolve(picking\_ids);  
                    }  
                });  
            return pickings;  
        }

        // if we have a specified picking id, we load that one, and we load the picking of the same type as the active list  
        // 有指定就显示指定picking\_id的记录  
        if (picking\_id) {  
            var loaded\_picking = new instance.web.Model('stock.picking')  
                .call('read', \[\[parseInt(picking\_id)\], \[\], new instance.web.CompoundContext()\])  
                .then(function (picking) {  
                    self.picking = picking\[0\];  
                    self.picking\_type\_id = picking\[0\].picking\_type\_id\[0\];  
                    return load\_picking\_list(self.picking.picking\_type\_id\[0\]);  
                });  
        } else {  
            // if we don't have a specified picking id, we load the pickings belong to the specified type, and then we take  
            // the first one of that list as the active picking  
            // 没有指定就显示默认第一条picking\_id的记录  
            var loaded\_picking = new $.Deferred();  
            load\_picking\_list(self.picking\_type\_id)  
                .then(function () {  
                    return new instance.web.Model('stock.picking').call('read', \[self.pickings\[0\], \[\], new instance.web.CompoundContext()\]);  
                })  
                .then(function (picking) {  
                    self.picking = picking;  
                    self.picking\_type\_id = picking.picking\_type\_id\[0\];  
                    loaded\_picking.resolve();  
                });  
        }

        return loaded\_picking.then(function () {  
            if (!\_.isEmpty(self.locations)) {  
                return $.when();  
            }  
            return new instance.web.Model('stock.location').call('search', \[\[\['usage', '=', 'internal'\]\]\]).then(function (locations\_ids) {  
                return new instance.web.Model('stock.location').call('read', \[locations\_ids, \[\]\]).then(function (locations) {  
                    self.locations = locations;  
                });  
            });  
        }).then(function () {  
            return new instance.web.Model('stock.picking').call('check\_group\_pack').then(function (result) {  
                return self.show\_pack = result;  
            });  
        }).then(function () {  
            return new instance.web.Model('stock.picking').call('check\_group\_lot').then(function (result) {  
                return self.show\_lot = result;  
            });  
        }).then(function () {  
            if (self.picking.pack\_operation\_exist === false) {  
                self.picking.recompute\_pack\_op = false;  
                return new instance.web.Model('stock.picking').call('do\_prepare\_partial', \[\[self.picking.id\]\]);  
            }  
        }).then(function () {  
            return new instance.web.Model('stock.pack.operation').call('search', \[\[\['picking\_id', '=', self.picking.id\]\]\])  
        }).then(function (pack\_op\_ids) {  
            return new instance.web.Model('stock.pack.operation').call('read', \[pack\_op\_ids, \[\], new instance.web.CompoundContext()\])  
        }).then(function (operations) {  
            self.packoplines = operations;  
            var package\_ids = \[\];

            for (var i = 0; i < operations.length; i++) {  
                if (!\_.contains(package\_ids, operations\[i\].result\_package\_id\[0\])) {  
                    if (operations\[i\].result\_package\_id\[0\]) {  
                        package\_ids.push(operations\[i\].result\_package\_id\[0\]);  
                    }  
                }  
            }  
            return new instance.web.Model('stock.quant.package').call('read', \[package\_ids, \[\], new instance.web.CompoundContext()\])  
        }).then(function (packages) {  
            self.packages = packages;  
        }).then(function () {  
            return new instance.web.Model('product.ul').call('search', \[\[\]\])  
        }).then(function (uls\_ids) {  
            return new instance.web.Model('product.ul').call('read', \[uls\_ids, \[\]\])  
        }).then(function (uls) {  
            self.uls = uls;  
        });  
    },  
    start: function () {  
        this.\_super();  
        var self = this;  
        instance.webclient.set\_content\_full\_screen(true);  
        this.barcode\_scanner.connect(function (ean) {  
            self.scan(ean);  
        });

        this.$('.js\_pick\_quit').click(function () {  
            self.quit();  
        });  
        this.$('.js\_pick\_prev').click(function () {  
            self.picking\_prev();  
        });  
        this.$('.js\_pick\_next').click(function () {  
            self.picking\_next();  
        });  
        this.$('.js\_pick\_menu').click(function () {  
            self.menu();  
        });  
        this.$('.js\_reload\_op').click(function () {  
            self.reload\_pack\_operation();  
        });  
        // -----------------------------------------------  
        $(document).click(function(e) {  
            self.select\_id =  $(e.target).attr("id")  
        });

        $.when(this.loaded).done(function () {  
            // 实例化部件PickingEditorWidget  
            self.picking\_editor = new extmodule.PickingEditorWidget(self);  
            self.picking\_editor.replace(self.$('.oe\_placeholder\_picking\_editor'));

            if (self.picking.id === self.pickings\[0\]) {  
                self.$('.js\_pick\_prev').addClass('disabled');  
            } else {  
                self.$('.js\_pick\_prev').removeClass('disabled');  
            }

            if (self.picking.id === self.pickings\[self.pickings.length - 1\]) {  
                self.$('.js\_pick\_next').addClass('disabled');  
            } else {  
                self.$('.js\_pick\_next').removeClass('disabled');  
            }  
            if (self.picking.recompute\_pack\_op) {  
                self.$('.oe\_reload\_op').removeClass('hidden');  
            }  
            else {  
                self.$('.oe\_reload\_op').addClass('hidden');  
            }  
            if (!self.show\_pack) {  
                self.$('.js\_pick\_pack').addClass('hidden');  
            }  
            if (!self.show\_lot) {  
                self.$('.js\_create\_lot').addClass('hidden');  
            }

        }).fail(function (error) {  
            console.log(error);  
        });

    },

    on\_searchbox: function (query) {  
        var self = this;  
        self.picking\_editor.on\_searchbox(query.toUpperCase());  
    },  
    on\_searchbox2: function (query) {  
        var self = this;  
        self.picking\_editor.on\_searchbox2(query.toUpperCase());  
    },  
    // reloads the data from the provided picking and refresh the ui.  
    // (if no picking\_id is provided, gets the first picking in the db)  
    refresh\_ui: function (picking\_id) {  
        var self = this;  
        var remove\_search\_filter = "";  
        if (self.picking.id === picking\_id) {  
            remove\_search\_filter = self.$('.oe\_searchbox').val();  
        }  
        return this.load(picking\_id)  
            .then(function () {  
                self.picking\_editor.remove\_blink();  
                self.picking\_editor.renderElement();  
                if (!self.show\_pack) {  
                    self.$('.js\_pick\_pack').addClass('hidden');  
                }  
                if (!self.show\_lot) {  
                    self.$('.js\_create\_lot').addClass('hidden');  
                }  
                if (self.picking.recompute\_pack\_op) {  
                    self.$('.oe\_reload\_op').removeClass('hidden');  
                }  
                else {  
                    self.$('.oe\_reload\_op').addClass('hidden');  
                }

                if (self.picking.id === self.pickings\[0\]) {  
                    self.$('.js\_pick\_prev').addClass('disabled');  
                } else {  
                    self.$('.js\_pick\_prev').removeClass('disabled');  
                }

                if (self.picking.id === self.pickings\[self.pickings.length - 1\]) {  
                    self.$('.js\_pick\_next').addClass('disabled');  
                } else {  
                    self.$('.js\_pick\_next').removeClass('disabled');  
                }  
                if (remove\_search\_filter === "") {  
                    self.$('.oe\_searchbox').val('');  
                    self.on\_searchbox('');  
                }  
                else {  
                    self.$('.oe\_searchbox').val(remove\_search\_filter);  
                    self.on\_searchbox(remove\_search\_filter);  
                }

                // ------------------------------------  
                self.$('.oe\_searchbox2').val('');  
                self.on\_searchbox2('');  
            });  
    },  
    get\_header: function () {  
        if (this.picking) {  
            return this.picking.name;  
        } else {  
            return '';  
        }  
    },  
    menu: function () {  
        $.bbq.pushState('#action=stock.menu');  
        $(window).trigger('hashchange');  
    },  
    scan: function (ean) { //scans a barcode, sends it to the server, then reload the ui  
        var self = this;  
        var product\_visible\_ids = this.picking\_editor.get\_visible\_ids();

        return new instance.web.Model('stock.picking')  
            .call('process\_barcode\_from\_ui', \[self.picking.id, ean, product\_visible\_ids\])  
            .then(function (result) {

                if (result.filter\_loc !== false) {  
                    //check if we have receive a location as answer  
                    if (result.filter\_loc !== undefined) {

                            var modal\_loc\_hidden = self.$('#js\_LocationChooseModal').attr('aria-hidden');  
                            // var modal\_loc\_hidden2 = self.$("td\[id^='select'\]").attr('aria-hidden');  
                            if (modal\_loc\_hidden === "false") {  
                                var line = self.$('#js\_LocationChooseModal .js\_loc\_option\[data-loc-id=' + result.filter\_loc\_id + '\]').attr('selected', 'selected');  
                            }  
                            // -----------------------------------------------------  
                            else if (self.select\_id  != undefined ){  
                                var line = self.$('#' + self.select\_id + ' .js\_loc\_option\[data-loc-id=' + result.filter\_loc\_id + '\]').attr('selected', 'selected');  
                                self.$('#'+ self.select\_id).change();  
                            }  
                            else {  
                                self.$('.oe\_searchbox').val(result.filter\_loc);  
                                self.on\_searchbox(result.filter\_loc);  
                            }  
                    }  
                }

                console.log('oe\_searchbox2' + self.$('.oe\_searchbox2').val());

                if (result.operation\_id !== false) {  
                    self.refresh\_ui(self.picking.id).then(function () {  
                        return self.picking\_editor.blink(result.operation\_id);  
                    });  
                }  
            });  
    },

    scan\_product\_id: function (product\_id, increment, op\_id) { //performs the same operation as a scan, but with product id instead  
        var self = this;  
        return new instance.web.Model('stock.picking')  
            .call('process\_product\_id\_from\_ui', \[self.picking.id, product\_id, op\_id, increment\])  
            .then(function (result) {  
                return self.refresh\_ui(self.picking.id);  
            });  
    },  
    pack: function () {  
        var self = this;  
        var pack\_op\_ids = self.picking\_editor.get\_current\_op\_selection(false);  
        if (pack\_op\_ids.length !== 0) {  
            return new instance.web.Model('stock.picking')  
                .call('action\_pack', \[\[\[self.picking.id\]\], pack\_op\_ids\])  
                .then(function (pack) {  
                    //TODO: the functionality using current\_package\_id in context is not needed anymore  
                    instance.session.user\_context.current\_package\_id = false;  
                    return self.refresh\_ui(self.picking.id);  
                });  
        }  
    },  
    drop\_down: function () {  
        var self = this;  
        var pack\_op\_ids = self.picking\_editor.get\_current\_op\_selection(true);  
        if (pack\_op\_ids.length !== 0) {  
            return new instance.web.Model('stock.pack.operation')  
                .call('action\_drop\_down', \[pack\_op\_ids\])  
                .then(function () {  
                    return self.refresh\_ui(self.picking.id).then(function () {  
                        if (self.picking\_editor.check\_done()) {  
                            return self.done();  
                        }  
                    });  
                });  
        }  
    },  
    done: function () {  
        var self = this;  
        return new instance.web.Model('stock.picking')  
            .call('action\_done\_from\_ui', \[self.picking.id, {'default\_picking\_type\_id': self.picking\_type\_id}\])  
            .then(function (new\_picking\_ids) {  
                if (new\_picking\_ids) {  
                    return self.refresh\_ui(new\_picking\_ids\[0\]);  
                }  
                else {  
                    return 0;  
                }  
            });  
    },  
    create\_lot: function (op\_id, lot\_name) {  
        var self = this;  
        return new instance.web.Model('stock.pack.operation')  
            .call('create\_and\_assign\_lot', \[parseInt(op\_id), lot\_name\])  
            .then(function () {  
                return self.refresh\_ui(self.picking.id);  
            });  
    },  
    change\_location: function (op\_id, loc\_id, is\_src\_dst) {  
        var self = this;  
        var vals = {'location\_dest\_id': loc\_id};

        if (is\_src\_dst) {  
            vals = {'location\_id': loc\_id};  
        }  
        return new instance.web.Model('stock.pack.operation')  
            .call('write', \[op\_id, vals\])  
            .then(function () {  
                return self.refresh\_ui(self.picking.id);  
            });  
    },  
    print\_package: function (package\_id) {  
        var self = this;  
        return new instance.web.Model('stock.quant.package')  
            .call('action\_print', \[\[package\_id\]\])  
            .then(function (action) {  
                return self.do\_action(action);  
            });  
    },  
    print\_picking: function () {  
        var self = this;  
        return new instance.web.Model('stock.picking.type').call('read', \[\[self.picking\_type\_id\], \['code'\], new instance.web.CompoundContext()\])  
            .then(function (pick\_type) {  
                return new instance.web.Model('stock.picking').call('do\_print\_picking', \[\[self.picking.id\]\])  
                    .then(function (action) {  
                        return self.do\_action(action);  
                    });  
            });  
    },  
    picking\_next: function () {  
        for (var i = 0; i < this.pickings.length; i++) {  
            if (this.pickings\[i\] === this.picking.id) {  
                if (i < this.pickings.length - 1) {  
                    $.bbq.pushState('picking\_id=' + this.pickings\[i + 1\]);  
                    this.refresh\_ui(this.pickings\[i + 1\]);  
                    return;  
                }  
            }  
        }  
    },  
    picking\_prev: function () {  
        for (var i = 0; i < this.pickings.length; i++) {  
            if (this.pickings\[i\] === this.picking.id) {  
                if (i > 0) {  
                    $.bbq.pushState('picking\_id=' + this.pickings\[i - 1\]);  
                    this.refresh\_ui(this.pickings\[i - 1\]);  
                    return;  
                }  
            }  
        }  
    },  
    delete\_package\_op: function (pack\_id) {  
        var self = this;  
        return new instance.web.Model('stock.pack.operation').call('search', \[\[\['result\_package\_id', '=', pack\_id\]\]\])  
            .then(function (op\_ids) {  
                return new instance.web.Model('stock.pack.operation').call('write', \[op\_ids, {'result\_package\_id': false}\])  
                    .then(function () {  
                        return self.refresh\_ui(self.picking.id);  
                    });  
            });  
    },  
    set\_operation\_quantity: function (quantity, op\_id) {  
        var self = this;  
        if (quantity >= 0) {  
            return new instance.web.Model('stock.pack.operation')  
                .call('write', \[\[op\_id\], {'qty\_done': quantity}\])  
                .then(function () {  
                    self.refresh\_ui(self.picking.id);  
                });  
        }

    },  
    set\_package\_pack: function (package\_id, pack) {  
        var self = this;  
        return new instance.web.Model('stock.quant.package')  
            .call('write', \[\[package\_id\], {'ul\_id': pack}\]);  
        return;  
    },  
    reload\_pack\_operation: function () {  
        var self = this;  
        return new instance.web.Model('stock.picking')  
            .call('do\_prepare\_partial', \[\[self.picking.id\]\])  
            .then(function () {  
                self.refresh\_ui(self.picking.id);  
            });  
    },  
    quit: function () {  
        this.destroy();  
        return new instance.web.Model("ir.model.data").get\_func("search\_read")(\[\['name', '=', 'action\_picking\_type\_form'\]\], \['res\_id'\]).pipe(function (res) {  
            window.location = '/web#action=' + res\[0\]\['res\_id'\];  
        });  
    },  
    destroy: function () {  
        this.\_super();  
        // this.disconnect\_numpad();  
        this.barcode\_scanner.disconnect();  
        instance.webclient.set\_content\_full\_screen(false);  
    },  
});  
openerp.web.client\_actions.add('stock.ui', 'instance.ext\_stock.PickingMainWidget');

extmodule.BarcodeScanner = instance.web.Class.extend({  
    connect: function (callback) {  
        var code = "";  
        var timeStamp = 0;  
        var timeout = null;

        this.handler = function (e) {

            if (e.which === 13) { //ignore returns  which 属性指示按了哪个键或按钮  
                return;  
            }  
            if (timeStamp + 50 < new Date().getTime()) {  
                code = "";  
            }

            timeStamp = new Date().getTime();   //getTime() 返回从1970年1月1日至今的毫秒数  
            clearTimeout(timeout);              // setTimeout()和clearTimeout()一起使用,停止计时器

            code += String.fromCharCode(e.which);  // fromCharCode可接受一个指定的Unicode值,然后返回一个字符串

            timeout = setTimeout(function () {  
                if (code.length >= 3) {  
                    // console.log('callback:>>>>' +callback);  
                    callback(code);  
                }  
                code = "";  
            }, 100);  
        };

        $('body').on('keypress', this.handler);  
    },  
    disconnect: function () {  
        $('body').off('keypress', this.handler);  
    },  
});

}

openerp.ext_stock = function (openerp) {
openerp.ext_stock = openerp.ext_stock || {};
openerp_ext_picking_widgets(openerp);
}