odoo表单页明细的动态创建和删除

引子:

odoo页面上控制的最小单位是字段,可以通过odoo提供的属性控制字段的必填、只读、隐藏等

但是对于form表单下的tree视图,总体来讲仍然是一个字段,

可以通过tree视图的属性create="1" delete="1" 来控制明细信息的创建和删除,但是不能单独控制具体某一行的属性。

 

思考过程:

能不能在页面上设置一个控制开关,开启的时候明细行就能添加和删除,关闭时就不能,

进一步深入,如果能通过开关字段控制明细行属性,能不能通过字段带出明细数据,并禁止添加和删除呢,

又不能是仅仅的只读,要可编辑才行!

 

 解决过程:

我们把上述过程分成两步来实现,

1, 根据主表字段带出明细信息。

首先在后端使用onchange监听能带出明细的字段,确保该字段数据内容正确,

同时拿到数据之后通过模型查询要带出的数据,对数据进行格式化

按着odoo默认的关联关系来绑定,就能实现明细的带出啦。

举个小例子,模型test是主表,其中name_rel就是关联字段,通过姓名带出信息

后端代码实现:

@api.onchange('name_a')    监听姓名字段
def _name_a(self):
    args_list = []
    self.name_rel = None 把关联关系接触,每次触发清楚上次的明细
   if self.name_a:
        purchase_detail = [{             可以通过sql查询,或者接口获取,拿到想要的明细数据
            'name_b': '打篮球', 
        'time_b': '2年'
      }]
        for detail in purchase_detail:
            num = 1
        args = {
                'name_b': detail['name_b'],
           'time_b': detail['time_b'],
        }
            args_list.append((0, 0, args))         建立odoo规定的关联关系!!
            num += 1
 self.res_sale_product_detail = args_list          给关联字段赋值

带出问题顺利解决!

 

2,根据主表字段控制明细表的添加和删除功能。

如果说带出功能是后端的问题,那么隐藏添加明细和删除就是前端来控制的。

明细表虽然是表数据,但是在主模型里,依然是一个一对多的字段,只要是字段就能使用widget控制,我们就顺着这条思路进行探索。

在痛苦的研磨源码之后,我们发现页面每次改动之后都会调用前端基础控件FieldX2Many的reset方法;

虽然我们是拓展FieldOne2Many 方法,但是FieldOne2Many方法也是拓展自基础模型FieldX2Many,所以我们可以直接在新方法里重写reset方法,

当然在起初进入页面的时候,可以修改基础的init属性,保证页面初始加载,又或是动态改变之后,依然能生效。

var hideCreateDelete = FieldOne2Many.extend({
        init: function (parent, name, record, options) {
            this._super.apply(this, arguments);
            let contract_no = record.data['contract_no'];
            var arch = this.view && this.view.arch;
            if (contract_no) {
                if (arch) {
                    this.activeActions.create = false;
                    this.activeActions.delete = false;
                    this.editable = arch.attrs.editable;
                }
            } else {
                if (arch) {
                    this.activeActions.create = true;
                    this.activeActions.delete = true;
                    this.editable = arch.attrs.editable;
                }
            }
        },
        reset: function (record, ev, fieldChanged) {
            let contract_no = record.data['contract_no'];
            const oldCanCreate = this.canCreate;
            const oldCanDelete = this.canDelete;
            const oldCanLink = this.canLink;
            const oldCanUnlink = this.canUnlink;
            var arch = this.view && this.view.arch;
            if (contract_no) {
                this.activeActions.create = false;
                this.activeActions.delete = false;
                this.editable = arch.attrs.editable;
            } else {
                this.activeActions.create = true;
                this.activeActions.delete = true;
                this.editable = arch.attrs.editable;
            }
            this._computeAvailableActions(record);
            const actionsChanged =
                this.canCreate !== oldCanCreate ||
                this.canDelete !== oldCanDelete ||
                this.canLink !== oldCanLink ||
                this.canUnlink !== oldCanUnlink;

            // If 'fieldChanged' is false, it means that the reset was triggered by
            // the 'resetOnAnyFieldChange' mechanism. If it is the case, if neither
            // the modifiers (so the visible columns) nor the available actions
            // changed, the reset is skipped.
            if (!fieldChanged && !actionsChanged) {
                var newEval = this._evalColumnInvisibleFields();
                if (_.isEqual(this.currentColInvisibleFields, newEval)) {
                    this._reset(record, ev); // update the internal state, but do not re-render
                    return Promise.resolve();
                }
            } else if (ev && ev.target === this && ev.data.changes && this.view.arch.tag === 'tree') {
                var command = ev.data.changes[this.name];
                // Here, we only consider 'UPDATE' commands with data, which occur
                // with editable list view. In order to keep the current line in
                // edition, we call confirmUpdate which will try to reset the widgets
                // of the line being edited, and rerender the rest of the list.
                // 'UPDATE' commands with no data can be ignored: they occur in
                // one2manys when the record is updated from a dialog and in this
                // case, we can re-render the whole subview.
                if (command && command.operation === 'UPDATE' && command.data) {
                    var state = record.data[this.name];
                    var fieldNames = state.getFieldNames({viewType: 'list'});
                    this._reset(record, ev);
                    return this.renderer.confirmUpdate(state, command.id, fieldNames, ev.initialEvent);
                }
            }
            this._reset(record, ev);
            return this._super.apply(this, arguments);
        },
    });

想要实现按条件显示添加明细和删除,我们就要从内部理解关联字段的属性,

this.canCreate是可以创建的,同理canDelete就是能否删除。

最后我们给明细字段绑定上自定义的widget,一起在页面上见证成果:

编辑模式下,添加明细和删除按钮已经隐藏啦~~

 

-------------------已在公司公众号推送--------------------

 
posted @ 2023-02-17 14:51  木_糖  阅读(207)  评论(0编辑  收藏  举报