如何从 JavaScript 承诺(python 调用)中获取变量,避免 Odoo 中的挂起状态?

How can I get a variable from a JavaScript promises (python calls), avoiding the pending state in Odoo?

来自销售点模块的原始代码

point_of_sale模块中有一个对象列表如下

module.PosModel = Backbone.Model.extend({

    models: {

        // [...]

        {
            model:  'pos.session',
            fields: ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number','login_number'],
            domain: function(self){ return [['state','=','opened'],['user_id','=',self.session.uid]]; },
            loaded: function(self,pos_sessions){
                self.pos_session = pos_sessions[0]; 

                var orders = self.db.get_orders();
                for (var i = 0; i < orders.length; i++) {
                    self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1);
                }
            },
        },
        {
            model:  'product.product',
            fields: ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code', 
                        'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
                        'product_tmpl_id'],
            domain: [['sale_ok','=',true],['available_in_pos','=',true]],
            context: function(self){ return { pricelist: self.pricelist.id, display_default_code: false }; },
            loaded: function(self, products){
                self.db.add_products(products);
        },

        // [...]

    }

然后数据的信息就是这样加载的

load_server_data: function(){
    var self = this;
    var loaded = new $.Deferred();
    var progress = 0;
    var progress_step = 1.0 / self.models.length;
    var tmp = {}; // this is used to share a temporary state between models loaders

    function load_model(index){
        if(index >= self.models.length){
            loaded.resolve();
        }else{
            var model = self.models[index];
            self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
            var fields =  typeof model.fields === 'function'  ? model.fields(self,tmp)  : model.fields;
            var domain =  typeof model.domain === 'function'  ? model.domain(self,tmp)  : model.domain;
            var context = typeof model.context === 'function' ? model.context(self,tmp) : model.context; 
            var ids     = typeof model.ids === 'function'     ? model.ids(self,tmp) : model.ids;
            progress += progress_step;


            if( model.model ){
                if (model.ids) {
                    var records = new instance.web.Model(model.model).call('read',[ids,fields],context);
                } else {
                    var records = new instance.web.Model(model.model).query(fields).filter(domain).context(context).all()
                }

// [...]

我试过的。先试试

所以,我想更改 product.product 模型的域字段。我正在尝试这个

if (typeof jQuery === 'undefined') { throw new Error('Product multi POS needs jQuery'); }

+function ($) {
    'use strict';

    openerp.pos_product_multi_shop = function(instance, module) {
        var PosModelParent = instance.point_of_sale.PosModel;
        instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
            load_server_data: function(){
                console.log('-- LOAD SERVER DATA');
                var self = this; 

                self.models.forEach(function(elem) {
                    if (elem.model == 'product.product') {
                        // return [['id', 'in', [2]]];    // if I return this domain it works well
                        domain_loaded = function() {
                            return new instance.web.Model('product.product').call(
                                'get_available_in_pos_ids',
                                [self.pos_session.config_id[0]],
                            )
                        }
                        elem.domain = $.when(domain_loaded);
                    }
                })

                var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
                return loaded;
            },
        });
    }
}(jQuery);

如果我直接 return 一个域就可以了。但是,如果我将其替换为使用 call 调用 python 函数的函数,则域加载不正确:[['sale_ok','=',true],['available_in_pos','=',true]]。我试过使用 $.when 和不使用它,但它不起作用。

另外elem.domain必须是一个函数,因为self.pos_session只有前面的模型信息全部执行完才会存在

第二次尝试

我也尝试过以下代码:

if (elem.model == 'product.product') {
    // return [['id', 'in', [2]]];    // if I return the domain like this it works
    console.log('>> OLD DOMAIN')
    console.log(elem.domain);
    elem.domain = function() {
        console.log('>>> PRODUCT SESSION');
        console.log(self.pos_session);
        var product_product_obj = new instance.web.Model('product.product');
        return product_product_obj.call(
            'get_available_in_pos_ids',
            [self.pos_session.config_id[0]],
        )
    }
    console.log('>> NEW DOMAIN')
    console.log(elem.domain);
}

所以首先打印“>> OLD DOMAIN”,然后打印“>> NEW DOMAIN”,最后打印“>>> PRODUCT SESSION”。所以函数被执行了。但是域 return 编辑得不好。

第三次尝试。随着 "then"

我不能使用then因为我需要做变量赋值。但另一方面,分配做得很好,因为当我打印新域时,函数出现在日志中。

即使我使用 then 我也从 python

得到了很好的结果
var domain_return = product_product_obj.call(
    'get_available_in_pos_ids',
    [self.pos_session.config_id[0]],
).then(function(result) {
    console.log('>> RESULT: ');
    console.log(result)
});

我也尝试过其他承诺,但我得到了一个被忽略的未决结果,并且显示了所有产品

elem.domain = function() {
    return new Promise(function next(resolve, reject) {
        console.log('>>> PRODUCT SESSION');
        console.log(self.pos_session);
        var product_product_obj = new instance.web.Model('product.product');
        var domain_return = product_product_obj.call(
            'get_available_in_pos_ids',
            [self.pos_session.config_id[0]],
        ).then(function(result) {
            console.log('>> RETURN: ');
            console.log(result);
            resolve(result);
        });
        console.log('>> DOMAIN RETURN: ');
        console.log(domain_return);
    });
}

在不调用 python 函数的情况下计算对象的其余域。所以我不能从其他地方复制一个例子

那么,有没有办法避免pending result呢?我还不能使用 async/await

也许让它同步会有帮助,但我知道应该避免这种情况

最后我找到了一个解决方法来覆盖已加载所有产品的已加载函数

var PosModelParent = instance.point_of_sale.PosModel;
instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
    load_server_data: function(){
        let self = this;
        self.models.forEach(function(elem) {
            if (elem.model == 'product.product') {
                elem.fields = ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code', 
                'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
                'product_tmpl_id', 'available_in_pos_ids'];
                elem.loaded = function(self, products){
                    console.log('>> PRODUCTS: ');
                    console.log(products);
                    var shop_id = self.pos_session.config_id[0];                            
                    var new_products = [];
                    products.forEach(function(prod) {
                        if (prod.available_in_pos_ids.includes(shop_id)) {
                            new_products.push(prod);
                        }
                    })
                    self.db.add_products(new_products);
                }
            }
        })
        var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
        return loaded;
    },
});