在 Odoo10 中更新树中的兄弟行
Update sibling rows in a tree in Odoo10
我有一个模型,其中包含一个 parent 和几个 children。
parent 有一个计算字段 "product_remaining",它是根据 children ("lot_qty") 中任何一个不同字段的修改而更新的。
字段 "product_remaining" 显示在所有 children 的树中,使用 children 模型中的 "related" 字段。
一切正常,除了树仅更新已完成修改的行中的 "product_remaining" 字段,而不会更新任何其他行。
此外,如果我单击修改之前更改未更新的行,并且不修改它就离开,则该行会正确更新。
有什么方法可以 update/refresh 树视图中所有受影响的行,而不仅仅是已进行更改的行?
我正在使用 Odoo v10。
相关代码如下:
Python:
class Te2PackLotOperation(models.TransientModel):
_name = 'te2.pack.lot.operation'
parent_id = fields.Many2one('te2.pack.product.operation', readonly=True, ondelete='cascade')
product_id = fields.Many2one('product.product', 'Product', related='parent_id.product_id')
product_qty = fields.Float('Total', readonly=True, related='parent_id.product_qty')
product_remaining = fields.Float('Remaining', readonly=True, related='parent_id.product_remaining')
lot_id = fields.Many2one('stock.production.lot')
lot_qty = fields.Float('Done')
packing = fields.Char('Packing')
@api.onchange('lot_qty')
def onchange_lot_qty(self):
rec = self._origin
rec.write({'lot_qty': self.lot_qty})
class Te2PackProductOperation(models.TransientModel):
_name = 'te2.pack.product.operation'
product_id = fields.Many2one('product.product', 'Product', readonly=True)
product_qty = fields.Float('Total', readonly=True)
product_remaining = fields.Float('Remaining', readonly=True, stored=False, compute='_product_remaining_get')
lot_ids = fields.One2many('te2.pack.lot.operation', 'parent_id')
def _product_remaining_get(self):
for rec in self:
already_done = 0
for lot in rec.lot_ids:
already_done += lot.lot_qty
rec.product_remaining = rec.product_qty - already_done
XML:
<record id="te2_stock_picking_advanced_view_form" model="ir.ui.view">
<field name="name">te2.pack.operation.form</field>
<field name="model">te2.pack.product.operation</field>
<field name="arch" type="xml">
<form string="Advanced Stock Picking">
<field name="lot_ids">
<tree editable="bottom" create="false" delete="false"
decoration-danger="product_remaining!=0" decoration-success="product_remaining==0"
default_order="product_id">
<field name="product_id"/>
<field name="product_qty"/>
<field name="product_remaining"/>
<field name="lot_id"
domain="[('product_id','=', product_id)]"
/>
<field name="use_date"/>
<field name="lot_qty"/>
<field name="packing"/>
<button name="add_lot" string="Lot Split" type="object" icon="fa-list"/>
</tree>
</field>
<footer>
<button name="save" string="Save" type="object" class="oe_highlight" />
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
好的,经过一些挖掘,我设法通过扩展 JavaScript 类.
解决了这个问题
如果有人感兴趣,代码如下:
odoo.define('te2_base', function(require) {
"use strict";
var core = require('web.core');
//The ListView. This view is used by the one2many field (see below)
var One2ManyUpdateAllListView = core.one2many_view_registry.get('list').extend({
init: function(viewmanager) {
var result = this._super.apply(this, arguments);
//Register to the save event
this.on("save:after", this, this.reload_all_records);
return result;
},
//Similar to "reload_record" from "list_view.js", but supporting an array of records, and moving the
//100ms delay to the end of the operation, instead of one delay per record.
reload_records: function (records, options={}) {
var self = this;
var fields = this.fields_view.fields;
if (!options || !options.do_not_evict) {
//This is from "form_relational_widgets.js".
// Evict records from cache to ensure it will be reloaded correctly
records.each( function (record) {
self.dataset.evict_record(record.get('id'));
})
}
return this.dataset.read_ids(
records.map(function (record) {return record.get('id')}),
_.pluck(_(this.columns).filter(function (r) {
return r.tag === 'field';
}), 'name'),
{check_access_rule: true}
).then(function (upd_records) {
records.each(function (record) {
var values = upd_records.find(function (upd_r) {return upd_r['id'] == record.get('id')});
if (values == undefined) {
self.records.remove(record);
} else {
// _.each is broken if a field "length" is present
for (var key in values) {
if (fields[key] && fields[key].type === 'many2many')
record.set(key + '__display', false, {silent: true});
record.set(key, values[key], {silent: true});
}
record.trigger('change', record);
}
});
var def = $.Deferred();
setTimeout(function() {
def.resolve();
}, 100);
return def;
});
},
reload_all_records: function () {
return this.reload_records(this.records);
},
});
//The Field. We should override the listview used by the field, so it uses our custom view.
var FieldOne2ManyUpdateAll = core.form_widget_registry.get('one2many').extend({
init: function() {
var result = this._super.apply(this, arguments);
this.x2many_views.list=One2ManyUpdateAllListView;
return result;
}
});
//Register the new field, so we can use it from our XML files
core.form_widget_registry.add('te2_tree_update_all', FieldOne2ManyUpdateAll);
//Make the classes available for other modules.
return {
One2ManyUpdateAllListView: One2ManyUpdateAllListView,
FieldOne2ManyUpdateAll: FieldOne2ManyUpdateAll
};
});
并且只需在字段标签中添加widget属性即可使用(注意:应该添加到"field"标签中,而不是"tree"标签中):
<field name="lot_op_ids" options='{"always_reload": True}' widget="te2_tree_update_all">
我有一个模型,其中包含一个 parent 和几个 children。
parent 有一个计算字段 "product_remaining",它是根据 children ("lot_qty") 中任何一个不同字段的修改而更新的。
字段 "product_remaining" 显示在所有 children 的树中,使用 children 模型中的 "related" 字段。
一切正常,除了树仅更新已完成修改的行中的 "product_remaining" 字段,而不会更新任何其他行。
此外,如果我单击修改之前更改未更新的行,并且不修改它就离开,则该行会正确更新。
有什么方法可以 update/refresh 树视图中所有受影响的行,而不仅仅是已进行更改的行?
我正在使用 Odoo v10。
相关代码如下:
Python:
class Te2PackLotOperation(models.TransientModel):
_name = 'te2.pack.lot.operation'
parent_id = fields.Many2one('te2.pack.product.operation', readonly=True, ondelete='cascade')
product_id = fields.Many2one('product.product', 'Product', related='parent_id.product_id')
product_qty = fields.Float('Total', readonly=True, related='parent_id.product_qty')
product_remaining = fields.Float('Remaining', readonly=True, related='parent_id.product_remaining')
lot_id = fields.Many2one('stock.production.lot')
lot_qty = fields.Float('Done')
packing = fields.Char('Packing')
@api.onchange('lot_qty')
def onchange_lot_qty(self):
rec = self._origin
rec.write({'lot_qty': self.lot_qty})
class Te2PackProductOperation(models.TransientModel):
_name = 'te2.pack.product.operation'
product_id = fields.Many2one('product.product', 'Product', readonly=True)
product_qty = fields.Float('Total', readonly=True)
product_remaining = fields.Float('Remaining', readonly=True, stored=False, compute='_product_remaining_get')
lot_ids = fields.One2many('te2.pack.lot.operation', 'parent_id')
def _product_remaining_get(self):
for rec in self:
already_done = 0
for lot in rec.lot_ids:
already_done += lot.lot_qty
rec.product_remaining = rec.product_qty - already_done
XML:
<record id="te2_stock_picking_advanced_view_form" model="ir.ui.view">
<field name="name">te2.pack.operation.form</field>
<field name="model">te2.pack.product.operation</field>
<field name="arch" type="xml">
<form string="Advanced Stock Picking">
<field name="lot_ids">
<tree editable="bottom" create="false" delete="false"
decoration-danger="product_remaining!=0" decoration-success="product_remaining==0"
default_order="product_id">
<field name="product_id"/>
<field name="product_qty"/>
<field name="product_remaining"/>
<field name="lot_id"
domain="[('product_id','=', product_id)]"
/>
<field name="use_date"/>
<field name="lot_qty"/>
<field name="packing"/>
<button name="add_lot" string="Lot Split" type="object" icon="fa-list"/>
</tree>
</field>
<footer>
<button name="save" string="Save" type="object" class="oe_highlight" />
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
好的,经过一些挖掘,我设法通过扩展 JavaScript 类.
解决了这个问题如果有人感兴趣,代码如下:
odoo.define('te2_base', function(require) {
"use strict";
var core = require('web.core');
//The ListView. This view is used by the one2many field (see below)
var One2ManyUpdateAllListView = core.one2many_view_registry.get('list').extend({
init: function(viewmanager) {
var result = this._super.apply(this, arguments);
//Register to the save event
this.on("save:after", this, this.reload_all_records);
return result;
},
//Similar to "reload_record" from "list_view.js", but supporting an array of records, and moving the
//100ms delay to the end of the operation, instead of one delay per record.
reload_records: function (records, options={}) {
var self = this;
var fields = this.fields_view.fields;
if (!options || !options.do_not_evict) {
//This is from "form_relational_widgets.js".
// Evict records from cache to ensure it will be reloaded correctly
records.each( function (record) {
self.dataset.evict_record(record.get('id'));
})
}
return this.dataset.read_ids(
records.map(function (record) {return record.get('id')}),
_.pluck(_(this.columns).filter(function (r) {
return r.tag === 'field';
}), 'name'),
{check_access_rule: true}
).then(function (upd_records) {
records.each(function (record) {
var values = upd_records.find(function (upd_r) {return upd_r['id'] == record.get('id')});
if (values == undefined) {
self.records.remove(record);
} else {
// _.each is broken if a field "length" is present
for (var key in values) {
if (fields[key] && fields[key].type === 'many2many')
record.set(key + '__display', false, {silent: true});
record.set(key, values[key], {silent: true});
}
record.trigger('change', record);
}
});
var def = $.Deferred();
setTimeout(function() {
def.resolve();
}, 100);
return def;
});
},
reload_all_records: function () {
return this.reload_records(this.records);
},
});
//The Field. We should override the listview used by the field, so it uses our custom view.
var FieldOne2ManyUpdateAll = core.form_widget_registry.get('one2many').extend({
init: function() {
var result = this._super.apply(this, arguments);
this.x2many_views.list=One2ManyUpdateAllListView;
return result;
}
});
//Register the new field, so we can use it from our XML files
core.form_widget_registry.add('te2_tree_update_all', FieldOne2ManyUpdateAll);
//Make the classes available for other modules.
return {
One2ManyUpdateAllListView: One2ManyUpdateAllListView,
FieldOne2ManyUpdateAll: FieldOne2ManyUpdateAll
};
});
并且只需在字段标签中添加widget属性即可使用(注意:应该添加到"field"标签中,而不是"tree"标签中):
<field name="lot_op_ids" options='{"always_reload": True}' widget="te2_tree_update_all">