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,一起在页面上见证成果:
编辑模式下,添加明细和删除按钮已经隐藏啦~~
-------------------已在公司公众号推送--------------------