form视图监听扫码事件并跳转指定form页面

本文适用于odoo 10.0及以上版本

关于odoo的扫码功能,在前面的文章有过部分的探讨,有兴趣的可以看这里

虽然企业版支持库存调拨页面扫码、生产页面调拨扫码等功能,但这不意味这我们诸多免费玩家就没了这项福利,今天我们就详细地说一说如何将这项人民币玩家才有的功能迁移到我们社区版上面。

barcodes模块

我们上面之所以说的那么理直气壮,多半的原因是odoo在社区版里已经将扫码的基础支持集成了,这就像是建房子的基石,odoo已经在社区版中给你做好了,它要收费的是它建造的别墅,而我们这些屌丝群体,可以在这块基石上建造我们的砖房、木房等等。那么这里的基石就是barcodes模块。

form页面添加对扫码事件的支持

在任何一个form页面,想要支持扫码事件,就必须要引入一个_barcode_scanned字段,这个字段不需要我们自己手动创建,我们只需要在我们的model中继承自barcodes.barcode_events_mixin类即可,像这样:

1
2
3
class mrp_production(models.Model):
_name = "mrp.production"
_inherit = ["mrp.production", "barcodes.barcode_events_mixin"]

然后,你就可以在你的页面中添加一个 <field name="_barcode_scanned" widget="barcode_handler"/>,这里的widget默认是barcodes里模块的widget,你可以编写自己的widget,这个稍后会讲。页面中添加了对扫码事件的支持,还需要一步,就是如何处理这个扫描到的条码,也非常简单,就是你写自定义button一样,在后台代码中加入on_barcode_scanned方法,像这样:

1
2
def on_barcode_scanned(self, barcode):
pass

这样就可以处理你扫描到的条码了。

on_barcode_scanned事件处理中,你可以像普通的函数一样编写处理逻辑,但是你不能像button事件那样的返回一个action,目前至少原生的扫码是不支持页面跳转功能的。

自定义widget

自定义扫码事件的widget和编写一个普通的widget基本一致,只不过如果你想要改变一些原生widget的特性(比如,barcode_handler不支持你在非编辑模式下扫码,它会不厌其烦地提示你点击编辑后才可以扫码),就需要继承barcodes.FormViewBarcodeHandler对象,并重写它的on_barcode_scanned事件。

一个重写的wiget大概有三个部分:
第一部分,继承widget的一些核心部件:

1
2
var core = require('web.core');
var FormViewBarcodeHandler = require('barcodes.FormViewBarcodeHandler');

第二部分,重写你要个边的特性及方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
on_barcode_scanned: function (barcode) {
var self = this;
var name = this.form_view.fields.name.get_value()
Session.rpc('/mrp_barcode/scan_to_open_report_from_form', {
barcode: barcode,
name: name
}).then(function (result) {
if (result.action) {
self.do_action(result.action);
} else if (result.warning) {
self.do_warn(result.warning);
}
});
}

第三部分,注册你的部件:

1
core.form_widget_registry.add('mrp_report_handler', MrpReportHandler);

整体的代码,大概长这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
odoo.define('tj.MrpReportHandler', function (require) {
"use strict";

var core = require('web.core');
var FormViewBarcodeHandler = require('barcodes.FormViewBarcodeHandler');
var Session = require('web.session');

var MrpReportHandler = FormViewBarcodeHandler.extend({

init: function (parent, context) {
if (parent.ViewManager.action) {
this.form_view_initial_mode = parent.ViewManager.action.context.form_view_initial_mode;
} else if (parent.ViewManager.view_form) {
this.form_view_initial_mode = parent.ViewManager.view_form.options.initial_mode;
}
return this._super.apply(this, arguments);
},

on_barcode_scanned: function (barcode) {
var self = this;
var name = this.form_view.fields.name.get_value()
Session.rpc('/mrp_barcode/scan_to_open_report_from_form', {
barcode: barcode,
name: name
}).then(function (result) {
if (result.action) {
self.do_action(result.action);
} else if (result.warning) {
self.do_warn(result.warning);
}
});
}
})

core.form_widget_registry.add('mrp_report_handler', MrpReportHandler);
});

不要忘了,要把你自己的widget替换掉视图页面中的widget

实现form页面跳转到另外一个页面

前面说过,原生的widget不支持页面跳转,那么我们如果就是要强行跳转该怎么办呢?
wigdet的部分已经写完了,可能有些人已经注意到了,我们的代码中,有这么一部分:

1
2
3
4
5
6
7
8
9
10
Session.rpc('/mrp_barcode/scan_to_open_report_from_form', {
barcode: barcode,
name: name
}).then(function (result) {
if (result.action) {
self.do_action(result.action);
} else if (result.warning) {
self.do_warn(result.warning);
}
});

这部分的意思就是,将扫到的条码,传给后台的controller,controller处理完逻辑之后,返回一个actioin或是一个warning,
如果是一个warning就会在页面右上角提示一个红框,如果是一个action就执行这个action。我们就是利用这个action来实现的页面跳转。

actioin的页面跳转是个老生常谈的问题了,写起来非常简单,就是返回一个包含ir.actions对象的字典,将这个字典返回给前端即可。

我们在controller里这么写:

1
2
3
4
5
6
7
8
9
10
@http.route('/mrp_barcode/scan_to_open_report_from_form', type="json", auth="user")
def open_report_from_form(self, barcode, name):
...
action = request.env.ref('tj.act_mrp_report').read()[0]
action['domain'] = [('id', 'in', mid)]
action['views'] = [
(request.env.ref('tj.mrp_report_form_view').id, 'form')]
action['res_id'] = mid
action['context'] = {'active_id': mid}
return {'action': action}

是不是跟你原来写的button跳转页面很像呢?
新技能get了嘛?还不赶紧去试一下。

非form页面的扫码事件

还有一种扫码事件,就是通过Qweb进行的响应,这种想对form要简单一些,大概的步骤就是,编写一个你的Qweb页面,然后在页面中加入对扫码事件的支持。

首先,创建widget并继承BarcodeHandlerMixin对象:

1
2
3
4
5
6
7
8
9
odoo.define('tj.MainMenu', function (require) {
"use strict";

var core = require('web.core');
var Model = require('web.Model');
var Widget = require('web.Widget');
var Dialog = require('web.Dialog');
var Session = require('web.session');
var BarcodeHandlerMixin = require('barcodes.BarcodeHandlerMixin');

其次,在init方法中,初始化父类支持:

1
2
3
4
5
6
init: function (parent, action) {
// Note: BarcodeHandlerMixin.init calls this._super.init, so there's no need to do it here.
// Yet, "_super" must be present in a function for the class mechanism to replace it with the actual parent method.
this._super;
BarcodeHandlerMixin.init.apply(this, arguments);
},

第三,加入对on_barcode_scanned事件的支持:

1
2
3
on_barcode_scanned: function (barcode) {
....
},

扫码是一个非常实用的功能,用得好能大大简化我们的工作流程,希望能本文能给你带来启发哦。

PS: 扫码事件的响应貌似是根据输入字符的间隔,如果间隔非常小,键盘也可以模拟条码枪的输入,这对于手上没有条码枪的同学来说非常实用,只是输入的字符不能太长。