2 openerp.base.form = function (openerp) {
4 openerp.base.views.add('form', 'openerp.base.FormView');
5 openerp.base.FormView = openerp.base.Controller.extend( /** @lends openerp.base.FormView# */{
7 * Indicates that this view is not searchable, and thus that no search
8 * view should be displayed (if there is one active).
13 * @param {openerp.base.Session} session the current openerp session
14 * @param {String} element_id this view's root element id
15 * @param {openerp.base.DataSet} dataset the dataset this view will work with
16 * @param {String} view_id the identifier of the OpenERP view object
18 init: function(view_manager, session, element_id, dataset, view_id) {
19 this._super(session, element_id);
20 this.view_manager = view_manager;
21 this.dataset = dataset;
22 this.model = dataset.model;
23 this.view_id = view_id;
24 this.fields_view = {};
26 this.widgets_counter = 0;
32 //this.log('Starting FormView '+this.model+this.view_id)
33 return this.rpc("/base/formview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
35 on_loaded: function(data) {
37 this.fields_view = data.fields_view;
39 var frame = new openerp.base.form.WidgetFrame(this, this.fields_view.arch);
41 this.$element.html(QWeb.render("FormView", { "frame": frame, "view": this }));
42 _.each(this.widgets, function(w) {
45 this.$element.find('div.oe_form_pager button[data-pager-action]').click(function() {
46 var action = $(this).data('pager-action');
47 self.on_pager_action(action);
50 this.$element.find('div.oe_form_buttons button.oe_form_button_save').click(this.do_save);
51 this.$element.find('div.oe_form_buttons button.oe_form_button_save_edit').click(this.do_save_edit);
52 this.$element.find('div.oe_form_buttons button.oe_form_button_cancel').click(this.do_cancel);
55 if (this.view_manager.sidebar) {
56 this.view_manager.sidebar.load_multi_actions();
59 on_record_loaded: function(record) {
61 this.datarecord = record;
62 for (var f in this.fields) {
63 this.fields[f].set_value(this.datarecord[f]);
65 this.on_form_changed();
68 this.log("No record received");
70 this.do_update_pager();
72 on_form_changed: function(widget) {
73 for (var w in this.widgets) {
79 // TODO: check if and trigger
80 // if (onchange for this field) {
81 // this.ready = false;
82 // rpc - process.callback ( this.ready = true; )
92 for (var f in this.fields) {
96 } else if (f.touched) {
97 values[f.name] = f.get_value();
103 this.log("About to save", values)
104 this.dataset.write(this.datarecord.id, values, this.on_saved);
107 do_save_edit: function() {
108 if (this.do_save()) {
109 this.switch_readonly();
112 do_show: function () {
113 this.dataset.fetch_index(this.fields_view.fields, this.on_record_loaded);
114 this.$element.show();
116 do_hide: function () {
117 this.$element.hide();
119 do_update_pager: function() {
120 var $pager = this.$element.find('div.oe_form_pager');
121 $pager.find("button[data-pager-action='first'], button[data-pager-action='previous']").attr('disabled', this.dataset.index == 0);
122 $pager.find("button[data-pager-action='next'], button[data-pager-action='last']").attr('disabled', this.dataset.index == this.dataset.ids.length - 1);
123 this.$element.find('span.oe_pager_index').html(this.dataset.index + 1);
124 this.$element.find('span.oe_pager_count').html(this.dataset.count);
126 on_pager_action: function(action) {
129 this.dataset.index = 0;
132 this.dataset.previous();
138 this.dataset.index = this.dataset.ids.length - 1;
141 this.dataset.fetch_index(this.fields_view.fields, this.on_record_loaded);
143 switch_readonly: function() {
145 switch_editable: function() {
147 on_invalid: function() {
149 _.each(this.fields, function(f) {
151 msg += "<li>" + f.string + "</li>";
155 this.notification.alert("The following fields are invalid :", msg);
157 on_saved: function(r) {
159 this.log("Record was not saved");
161 // Check response for exceptions, display error
162 this.notification['default']("Record saved", "The record #" + this.datarecord.id + " has been saved.");
165 do_search: function (domains, contexts, groupbys) {
167 on_action: function (action) {
172 openerp.base.form = {};
174 openerp.base.form.Widget = openerp.base.Controller.extend({
175 init: function(view, node) {
178 this.attrs = eval('(' + (this.node.attrs.attrs || '{}') + ')');
179 this.type = this.type || node.tag;
180 this.element_name = this.element_name || this.type;
181 this.element_id = [this.view.element_id, this.element_name, this.view.widgets_counter++].join("_");
183 this._super(this.view.session, this.element_id);
185 this.view.widgets[this.element_id] = this;
186 this.children = node.children;
187 this.colspan = parseInt(node.attrs.colspan || 1);
188 this.template = "Widget";
190 this.string = this.string || node.attrs.string;
191 this.help = this.help || node.attrs.help;
192 this.invisible = (node.attrs.invisible == '1');
195 this.$element = $('#' + this.element_id);
197 process_attrs: function() {
198 for (var a in this.attrs) {
199 this[a] = this.eval_attrs(this.attrs[a]);
202 eval_attrs: function(expr) {
204 for (var i = 0; i < expr.length; i++) {
206 if (ex.length == 1) {
211 var field = this.view.fields[ex[0]].value;
215 switch (op.toLowerCase()) {
218 stack.push(field == val);
222 stack.push(field != val);
225 stack.push(field < val);
228 stack.push(field > val);
231 stack.push(field <= val);
234 stack.push(field >= val);
237 stack.push(_.indexOf(val, field) > -1);
240 stack.push(_.indexOf(val, field) == -1);
243 this.log("Unsupported operator in attrs :", op);
247 for (var j = stack.length-1; j >- 1; j--) {
250 var result = stack[j + 1] || stack[j + 2];
251 stack.splice(j, 3, result);
254 var result = stack[j + 1] && stack[j + 2];
255 stack.splice(j, 3, result);
259 return _.indexOf(stack, false) == -1;
261 update_dom: function() {
262 this.$element.toggle(!this.invisible);
265 var template = this.template;
266 return QWeb.render(template, { "widget": this });
270 openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({
271 init: function(view, node) {
272 this._super(view, node);
273 this.template = "WidgetFrame";
274 this.columns = node.attrs.col || 4;
279 for (var i = 0; i < node.children.length; i++) {
280 var n = node.children[i];
281 if (n.tag == "newline") {
287 this.set_row_cells_with(this.table[this.table.length - 1]);
290 if (this.table.length) {
291 this.set_row_cells_with(this.table[this.table.length - 1]);
294 this.table.push(row);
299 set_row_cells_with: function(row) {
300 for (var i = 0; i < row.length; i++) {
302 if (w.is_field_label) {
305 row[i + 1].width = Math.round((100 / this.columns) * (w.colspan + 1) - 1) + '%';
307 } else if (w.width === undefined) {
308 w.width = Math.round((100 / this.columns) * w.colspan) + '%';
312 handle_node: function(n) {
313 var type = this.view.fields_view.fields[n.attrs.name] || {};
314 var widget_type = n.attrs.widget || type.type || n.tag;
315 var widget = new (openerp.base.form.widgets.get_object(widget_type)) (this.view, n);
316 if (n.tag == 'field' && n.attrs.nolabel != '1') {
317 var label = new (openerp.base.form.widgets.get_object('label')) (this.view, n);
318 label["for"] = widget;
319 this.add_widget(label);
321 this.add_widget(widget);
323 add_widget: function(w) {
325 var current_row = this.table[this.table.length - 1];
326 if (current_row.length && (this.x + w.colspan) > this.columns) {
327 current_row = this.add_row();
336 openerp.base.form.WidgetNotebook = openerp.base.form.Widget.extend({
337 init: function(view, node) {
338 this._super(view, node);
339 this.template = "WidgetNotebook";
341 for (var i = 0; i < node.children.length; i++) {
342 var n = node.children[i];
343 if (n.tag == "page") {
344 var page = new openerp.base.form.WidgetFrame(this.view, n);
345 this.pages.push(page);
350 this._super.apply(this, arguments);
351 this.$element.tabs();
355 openerp.base.form.WidgetSeparator = openerp.base.form.Widget.extend({
356 init: function(view, node) {
357 this._super(view, node);
358 this.template = "WidgetSeparator";
362 openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
363 init: function(view, node) {
364 this._super(view, node);
365 this.template = "WidgetButton";
369 openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
370 init: function(view, node) {
371 this.is_field_label = true;
372 this.element_name = 'label_' + node.attrs.name;
374 this._super(view, node);
376 this.template = "WidgetLabel";
379 render: function () {
380 if (this['for'] && this.type !== 'label') {
381 return QWeb.render(this.template, {widget: this['for']});
383 // Actual label widgets should not have a false and have type label
384 return QWeb.render(this.template, {widget: this});
388 openerp.base.form.Field = openerp.base.form.Widget.extend({
389 init: function(view, node) {
390 this.name = node.attrs.name;
391 this.value = undefined;
392 view.fields[this.name] = this;
393 this.type = node.attrs.widget || view.fields_view.fields[node.attrs.name].type;
394 this.element_name = "field_" + this.name + "_" + this.type;
396 this._super(view, node);
398 if (node.attrs.nolabel != '1' && this.colspan > 1) {
401 this.field = view.fields_view.fields[node.attrs.name];
402 this.string = node.attrs.string || this.field.string;
403 this.help = node.attrs.help || this.field.help;
404 this.nolabel = (node.attrs.nolabel == '1');
405 this.readonly = (node.attrs.readonly == '1');
406 this.required = (node.attrs.required == '1');
407 this.invalid = false;
408 this.touched = false;
410 set_value: function(value) {
412 this.invalid = false;
414 get_value: function() {
417 update_dom: function() {
418 this._super.apply(this, arguments);
419 this.$element.toggleClass('disabled', this.readonly);
420 this.$element.toggleClass('required', this.required);
421 this.$element.toggleClass('invalid', this.invalid);
423 on_ui_change: function() {
428 openerp.base.form.FieldChar = openerp.base.form.Field.extend({
429 init: function(view, node) {
430 this._super(view, node);
431 this.template = "FieldChar";
434 this._super.apply(this, arguments);
435 this.$element.find('input').change(this.on_ui_change);
437 set_value: function(value) {
438 this._super.apply(this, arguments);
439 var show_value = (value != null && value !== false) ? value : '';
440 this.$element.find('input').val(show_value);
442 update_dom: function() {
443 this._super.apply(this, arguments);
444 this.$element.find('input').attr('disabled', this.readonly);
446 on_ui_change: function() {
447 this._super.apply(this, arguments);
448 this.value = this.$element.find('input').val();
449 this.invalid = this.required && this.value == "";
450 this.view.on_form_changed(this);
454 openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({
457 openerp.base.form.FieldUrl = openerp.base.form.FieldChar.extend({
460 openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({
461 set_value: function(value) {
462 this._super.apply(this, arguments);
463 var show_value = (value != null && value !== false) ? value.toFixed(2) : '';
464 this.$element.find('input').val(value);
466 on_ui_change: function() {
467 this._super.apply(this, arguments);
468 this.value = this.$element.find('input').val().replace(/,/g, '.');
469 this.invalid = (this.required && this.value == "") || !this.value.match(/^\d(\.\d)?$/) ;
470 this.view.on_form_changed(this);
474 openerp.base.form.FieldDate = openerp.base.form.FieldChar.extend({
475 init: function(view, node) {
476 this._super(view, node);
477 this.template = "FieldDate";
480 this._super.apply(this, arguments);
481 this.$element.find('input').change(this.on_ui_change).datepicker({
482 dateFormat: 'yy-mm-dd'
485 set_value: function(value) {
486 this._super.apply(this, arguments);
487 var show_value = (value != null && value !== false) ? value : '';
488 this.$element.find('input').val(show_value);
490 on_ui_change: function() {
491 this._super.apply(this, arguments);
492 this.value = this.$element.find('input').val();
493 this.invalid = this.required && this.value == "";
494 this.view.on_form_changed(this);
498 openerp.base.form.FieldDatetime = openerp.base.form.FieldChar.extend({
499 init: function(view, node) {
500 this._super(view, node);
501 this.template = "FieldDatetime";
504 this._super.apply(this, arguments);
505 this.$element.find('input').change(this.on_ui_change).datetimepicker({
506 dateFormat: 'yy-mm-dd',
507 timeFormat: 'hh:mm:ss'
510 set_value: function(value) {
511 this._super.apply(this, arguments);
512 var show_value = (value != null && value !== false) ? value : '';
513 this.$element.find('input').val(show_value);
515 on_ui_change: function() {
516 this._super.apply(this, arguments);
517 this.value = this.$element.find('input').val();
518 this.invalid = this.required && this.value == "";
519 this.view.on_form_changed(this);
523 openerp.base.form.FieldText = openerp.base.form.Field.extend({
524 init: function(view, node) {
525 this._super(view, node);
526 this.template = "FieldText";
529 this._super.apply(this, arguments);
530 this.$element.find('textarea').change(this.on_ui_change);
532 set_value: function(value) {
533 this._super.apply(this, arguments);
534 var show_value = (value != null && value !== false) ? value : '';
535 this.$element.find('textarea').val(show_value);
537 update_dom: function() {
538 this._super.apply(this, arguments);
539 this.$element.find('textarea').attr('disabled', this.readonly);
541 on_ui_change: function() {
542 this._super.apply(this, arguments);
543 this.value = this.$element.find('textarea').val();
544 this.invalid = this.required && this.value == "";
545 this.view.on_form_changed(this);
549 openerp.base.form.FieldBoolean = openerp.base.form.Field.extend({
550 init: function(view, node) {
551 this._super(view, node);
552 this.template = "FieldBoolean";
556 this._super.apply(this, arguments);
557 this.$element.find('input').click(function() {
558 if ($(this)[0].checked != this.value) {
563 set_value: function(value) {
564 this._super.apply(this, arguments);
565 this.$element.find('input')[0].checked = value;
567 update_dom: function() {
568 this._super.apply(this, arguments);
569 this.$element.find('textarea').attr('disabled', this.readonly);
571 on_ui_change: function() {
572 this._super.apply(this, arguments);
573 this.value = this.$element.find('input').is(':checked');
574 this.invalid = this.required && !this.value;
575 this.view.on_form_changed(this);
579 openerp.base.form.FieldTextXml = openerp.base.form.Field.extend({
580 // to replace view editor
583 openerp.base.form.FieldSelection = openerp.base.form.Field.extend({
584 init: function(view, node) {
585 this._super(view, node);
586 this.template = "FieldSelection";
589 this._super.apply(this, arguments);
590 this.$element.find('select').change(this.on_ui_change);
592 set_value: function(value) {
593 this._super.apply(this, arguments);
594 if (value != null && value !== false) {
595 this.$element.find('select').val(value);
597 this.$element.find('select')[0].selectedIndex = 0;
600 update_dom: function() {
601 this._super.apply(this, arguments);
602 this.$element.find('select').attr('disabled', this.readonly);
604 on_ui_change: function() {
605 this._super.apply(this, arguments);
606 this.value = this.$element.find('select').val();
607 this.invalid = this.required && this.value == "";
608 this.view.on_form_changed(this);
612 openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
613 init: function(view, node) {
614 this._super(view, node);
615 this.template = "FieldMany2One";
617 set_value: function(value) {
618 this._super.apply(this, arguments);
620 if (value != null && value !== false) {
621 show_value = value[1];
622 this.value = value[0];
624 this.$element.find('input').val(show_value);
628 openerp.base.form.FieldOne2ManyDatasSet = openerp.base.DataSet.extend({
629 // Extends view manager
632 openerp.base.form.FieldOne2ManyViewManager = openerp.base.ViewManager.extend({
633 // Extends view manager
636 openerp.base.form.FieldOne2Many = openerp.base.form.Field.extend({
637 init: function(view, node) {
638 this._super(view, node);
639 this.template = "FieldOne2Many";
640 this.viewmanager = null;
641 this.operations = [];
645 this._super.apply(this, arguments);
646 this.log("o2m.start");
647 var action = { res_model: this.field.relation, views: [ [false,"list"] ] };
648 this.viewmanager = new openerp.base.ViewManagerAction(this.view.session, this.element_id, action);
650 set_value: function(value) {
652 this.log("o2m.set_value",value);
653 this.viewmanager.dataset.ids = value;
654 // this.viewmanager.views.list.controller.do_update();
656 get_value: function(value) {
657 return this.operations;
659 update_dom: function() {
660 this._super.apply(this, arguments);
661 this.$element.toggleClass('disabled', this.readonly);
662 this.$element.toggleClass('required', this.required);
664 on_ui_change: function() {
668 openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
669 init: function(view, node) {
670 this._super(view, node);
671 this.template = "FieldMany2Many";
675 openerp.base.form.FieldReference = openerp.base.form.Field.extend({
676 init: function(view, node) {
677 this._super(view, node);
678 this.template = "FieldReference";
683 * Registry of form widgets, called by :js:`openerp.base.FormView`
685 openerp.base.form.widgets = new openerp.base.Registry({
686 'group' : 'openerp.base.form.WidgetFrame',
687 'notebook' : 'openerp.base.form.WidgetNotebook',
688 'separator' : 'openerp.base.form.WidgetSeparator',
689 'label' : 'openerp.base.form.WidgetLabel',
690 'button' : 'openerp.base.form.WidgetButton',
691 'char' : 'openerp.base.form.FieldChar',
692 'email' : 'openerp.base.form.FieldEmail',
693 'url' : 'openerp.base.form.FieldUrl',
694 'text' : 'openerp.base.form.FieldText',
695 'date' : 'openerp.base.form.FieldDate',
696 'datetime' : 'openerp.base.form.FieldDatetime',
697 'selection' : 'openerp.base.form.FieldSelection',
698 'many2one' : 'openerp.base.form.FieldMany2One',
699 'many2many' : 'openerp.base.form.FieldMany2Many',
700 'one2many' : 'openerp.base.form.FieldOne2Many',
701 'one2many_list' : 'openerp.base.form.FieldOne2Many',
702 'reference' : 'openerp.base.form.FieldReference',
703 'boolean' : 'openerp.base.form.FieldBoolean',
704 'float' : 'openerp.base.form.FieldFloat',
705 'integer': 'openerp.base.form.FieldFloat',
706 'progressbar': 'openerp.base.form.FieldFloat',
707 'float_time': 'openerp.base.form.FieldFloat'
712 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: