Adding One2Many and Many2Many

This commit is contained in:
Nicolas Évrard committed 2013-05-23 16:17:56 +02:00
1 parent 2d1d2d33ce
commit 757eef7c2e
4 files changed
+507 -35

No files matched your search

+23 -10
View File
@@ -120,8 +120,7 @@
}
};
array.new_ = function(default_, id) {
var record = new Sao.Record(this.name, id);
record.model = this.model;
var record = new Sao.Record(this.model, id);
record.group = this;
if (default_) {
record.default_get();
@@ -159,26 +158,26 @@
var idx = this.indexOf(record);
if (record.id >= 0) {
if (remove) {
if (record in this.record_deleted) {
if (this.record_deleted.indexOf(record) != -1) {
this.record_deleted.splice(
this.record_deleted.indexOf(record), 1);
}
this.record_removed.push(record);
} else {
if (record in this.record_removed) {
if (this.record_removed.indexOf(record) != -1) {
this.record_removed.splice(
this.record_removed.indexOf(record), 1);
}
this.record_deleted.push(record);
}
}
if (record.parent) {
record.parent._changed.id = true;
if (record.group.parent) {
record.group.parent._changed.id = true;
}
if (modified) {
record._changed.id = true;
}
if (!(record.parent) || (record.id < 0) || force_remove) {
if (!(record.group.parent) || (record.id < 0) || force_remove) {
this._remove(record);
}
record.group.changed();
@@ -190,6 +189,14 @@
var idx = this.indexOf(record);
this.splice(idx, 1);
};
array.unremove = function(record) {
this.record_removed.splice(this.record_removed.indexOf(record), 1);
this.record_deleted.splice(this.record_deleted.indexOf(record), 1);
record.group.changed();
record.group.root_group().screens.forEach(function(screen) {
screen.display();
});
};
array.changed = function() {
if (!this.parent) {
return;
@@ -685,6 +692,12 @@
cancel: function() {
this._loaded = {};
this._changed = {};
},
deleted: function() {
return this.group.record_deleted.indexOf(this) != -1;
},
removed: function() {
return this.group.record_removed.indexOf(this) != -1;
}
});
@@ -782,8 +795,8 @@
},
get_context: function(record) {
var context = jQuery.extend({}, record.get_context());
if (record.parent) {
jQuery.extend(context, record.parent.get_context());
if (record.group.parent) {
jQuery.extend(context, record.group.parent.get_context());
}
// TODO eval context attribute
return context;
@@ -1302,7 +1315,7 @@
for (var i = 0, len = record._values[this.name].length; i < len;
i++) {
var record2 = group[i];
if (!record2.deleted || !record2.removed)
if (!record2.deleted() || !record2.removed())
result.push(record2.get_on_change_value());
}
return result;
+38 -11
View File
@@ -5,6 +5,10 @@
Sao.ScreenContainer = Sao.class_(Object, {
init: function(tab_domain) {
this.alternate_viewport = jQuery('<div/>', {
'class': 'screen-container'
});
this.alternate_view = false;
this.tab_domain = tab_domain || [];
this.el = jQuery('<div/>', {
'class': 'screen-container'
@@ -109,8 +113,14 @@
}
},
set: function(widget) {
this.content_box.children().detach();
this.content_box.append(widget);
if (this.alternate_view) {
this.alternate_viewport.children().detach();
// TODO test if widget is content_box widget
this.alternate_viewport.append(widget);
} else {
this.content_box.children().detach();
this.content_box.append(widget);
}
}
});
@@ -124,6 +134,7 @@
this.view_to_load = jQuery.extend([],
attributes.mode || ['tree', 'form']);
this.views = [];
this.exclude_field = attributes.exclude_field;
this.context = attributes.context || {};
this.new_group();
this.current_view = null;
@@ -143,9 +154,9 @@
this.domain_parser = null;
},
load_next_view: function() {
if (this.view_to_load) {
if (!jQuery.isEmptyObject(this.view_to_load)) {
var view_id;
if (this.view_ids) {
if (!jQuery.isEmptyObject(this.view_ids)) {
view_id = this.view_ids.shift();
}
var view_type = this.view_to_load.shift();
@@ -263,11 +274,13 @@
},
set_group: function(group) {
if (this.group) {
jQuery.extend(group.model.fields, this.group.model.fields);
this.group.screens.splice(
this.group.screens.indexOf(this), 1);
}
group.screens.push(this);
this.group = group;
this.model = group.model;
if (jQuery.isEmptyObject(group)) {
this.current_record = null;
} else {
@@ -285,11 +298,19 @@
if (this.views) {
this.search_active(['tree', 'graph', 'calendar'].indexOf(
this.current_view.view_type) > -1);
for (var i = 0; i < this.views.length; i++)
if (this.views[i])
for (var i = 0; i < this.views.length; i++) {
if (this.views[i]) {
this.views[i].display();
}
}
}
},
display_next: function() {
// TODO
},
display_previous: function() {
// TODO
},
default_row_activate: function() {
if ((this.current_view.view_type == 'tree') &&
this.current_view.keyword_open) {
@@ -394,6 +415,12 @@
// TODO test view modified
return false;
},
unremove: function() {
var records = this.current_view.selected_records();
records.forEach(function(record) {
record.group.unremove(record);
});
},
remove: function(delete_, remove, force_remove) {
var result = jQuery.Deferred();
var records = null;
@@ -418,14 +445,14 @@
var prms = [];
if (delete_) {
records.forEach(function(record) {
if (record.parent) {
prms.push(record.parent.save());
if (record.group.parent) {
prms.push(record.group.parent.save());
}
if (record in record.group.record_deleted) {
if (record.group.record_deleted.indexOf(record) != -1) {
record.group.record_deleted.splice(
record.group.record_deleted.indexOf(record), 1);
}
if (record in record.group.record_removed) {
if (record.group.record_removed.indexOf(record) != -1) {
record.group.record_removed.splice(
record.group.record_removed.indexOf(record), 1);
}
@@ -443,7 +470,7 @@
return result;
},
search_active: function(active) {
if (active && !this.parent) {
if (active && !this.group.parent) {
if (!this.fields_view_tree) {
this.model.execute('fields_view_get',
[false, 'tree'], this.context)
+424 -5
View File
@@ -134,9 +134,12 @@
'width': child.getAttribute('width'),
'orientation': child.getAttribute('orientation'),
'float_time': child.getAttribute('float_time'),
'pre_validate': child.getAttribute('pre_validate') == 1,
'completion': child.getAttribute('completion') == 1
};
if (name == this.screen.exclude_field) {
// TODO is it really the way to do it
return;
}
if (attributes.widget === null) {
attributes.widget = model.fields[name].description.type;
}
@@ -276,6 +279,11 @@
this.record.load(this.children_field).done(
add_children.bind(this));
}
if (this.record.deleted() || this.record.removed()) {
this.el.css('text-decoration', 'line-through');
} else {
this.el.css('text-decoration', 'inherit');
}
},
toggle_row: function() {
if (this.is_expanded()) {
@@ -508,7 +516,8 @@
'name': child.getAttribute('name'),
'widget': child.getAttribute('widget'),
'string': child.getAttribute('string'),
'states': child.getAttribute('states')
'states': child.getAttribute('states'),
'mode': child.getAttribute('mode')
};
['readonly', 'invisible'].forEach(function(name) {
if (child.getAttribute(name)) {
@@ -580,7 +589,11 @@
var name = attributes.name;
var text = attributes.string;
if (name in model.fields) {
// TODO exclude field
if (name == this.screen.exclude_field) {
container.add(
Number(node.getAttribute('colspan') || 1));
return;
}
if (!attributes.states && (name in model.fields)) {
attributes.states = model.fields[name].description.states;
}
@@ -642,8 +655,7 @@
},
_parse_field: function(model, node, container, attributes) {
var name = attributes.name;
// TODO exclude field
if (!(name in model.fields)) {
if (!(name in model.fields) || name == this.screen.exclude_field) {
container.add(
Number(node.getAttribute('colspan') || 1));
return;
@@ -780,6 +792,12 @@
} else {
this.screen.button(button.attributes);
}
},
selected_records: function() {
if (this.screen.current_record) {
return [this.screen.current_record];
}
return [];
}
});
@@ -948,6 +966,10 @@
return Sao.View.Form.Text;
case 'many2one':
return Sao.View.Form.Many2One;
case 'one2many':
return Sao.View.Form.One2Many;
case 'many2many':
return Sao.View.Form.Many2Many;
}
};
@@ -1519,4 +1541,401 @@
}
});
Sao.View.Form.One2Many = Sao.class_(Sao.View.Form.Widget, {
class_: 'form-one2many',
init: function(field_name, model, attributes) {
Sao.View.Form.One2Many._super.init.call(this, field_name, model,
attributes);
this.el = jQuery('<div/>', {
'class': this.class_
});
this.menu = jQuery('<div/>', {
'class': this.class_ + '-menu'
});
this.el.append(this.menu);
if (attributes.add_remove) {
this.wid_text = jQuery('<input/>', {
type: 'input'
});
// TODO add completion
this.but_add = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-plus'
},
label: 'Add'
});
this.but_add.click(this.add.bind(this));
this.menu.append(this.but_add);
this.but_remove = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-minus'
},
label: 'Remove'
});
this.but_remove.click(this.remove.bind(this));
this.menu.append(this.but_remove);
}
this.but_new = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-document'
},
label: 'New'
});
this.but_new.click(this.new_.bind(this));
this.menu.append(this.but_new);
this.but_open = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-folder-open'
},
label: 'Open'
});
this.but_open.click(this.open.bind(this));
this.menu.append(this.but_open);
this.but_del = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-trash'
},
label: 'Delete'
});
this.but_del.click(this.delete_.bind(this));
this.menu.append(this.but_del);
this.but_undel = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-arrowreturn-1-s'
},
label: 'Undelete'
});
this.but_undel.click(this.undelete.bind(this));
this.menu.append(this.but_undel);
this.but_previous = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-arrowthick-1-w'
},
label: 'Previous'
});
this.but_previous.click(this.previous.bind(this));
this.menu.append(this.but_previous);
this.label = jQuery('<span/>', {
'class': this.class_ + '-label'
});
this.label.text('(0, 0)');
this.menu.append(this.label);
this.but_next = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-arrowthick-1-e'
},
label: 'Next'
});
this.but_next.click(this.next.bind(this));
this.menu.append(this.but_next);
this.but_switch = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-newwin'
},
label: 'Switch'
});
this.but_switch.click(this.switch_.bind(this));
this.menu.append(this.but_switch);
this.content = jQuery('<div/>', {
'class': this.class_ + '-content'
});
this.el.append(this.content);
var modes = (attributes.mode || 'tree,form').split(',');
this.screen = new Sao.Screen(attributes.relation, {
mode: modes,
view_ids: (attributes.view_ids || '').split(','),
views_preload: attributes.views || {},
row_activate: this.activate.bind(this),
exclude_field: attributes.relation_field || null
});
this.prm = this.screen.switch_view(modes[0]).done(function() {
this.content.append(this.screen.screen_container.el);
}.bind(this));
// TODO sensitivity of buttons
},
display: function(record, field) {
Sao.View.Form.One2Many._super.display.call(this, record, field);
this.prm.done(function() {
if (!record) {
return;
}
if (field === undefined) {
this.screen.new_group();
this.screen.current_record = null;
this.screen.parent = true;
this.screen.display();
return;
}
var new_group = record.field_get_client(this.field_name);
if (new_group != this.screen.group) {
this.screen.set_group(new_group);
// TODO handle editable tree
// TODO set readonly, domain, size_limit
}
this.screen.display();
}.bind(this));
},
activate: function(event_) {
this.edit();
},
add: function(event_) {
},
remove: function(event_) {
// TODO model access
this.screen.remove(false, true, false);
},
new_: function(event_) {
// TODO model access
if (!this.validate()) {
return;
}
var context = jQuery.extend({},
this.field().get_context(this.screen.current_record));
// TODO sequence
if (this.screen.current_view.type == 'form' ||
this.screen.current_view.editable) {
this.screen.new_();
this.screen.current_view.el.prop('disabled', false);
} else {
var record = this.screen.current_record;
var field_size = record.expr_eval(this.attributes.size) || -1;
field_size -= this.field().get_eval(record);
var win = new Sao.Window.Form(this.screen, function() {}, {
new_: true,
many: field_size,
context: context
});
}
},
open: function(event_) {
this.edit();
},
delete_: function(event_) {
// TODO model access
this.screen.remove(false, false, false);
},
undelete: function(event_) {
this.screen.unremove();
},
previous: function(event_) {
if (!this.validate()) {
return;
}
this.screen.display_previous();
},
next: function(event_) {
if (!this.validate()) {
return;
}
this.screen.display_next();
},
switch_: function(event_) {
this.screen.switch_view();
// TODO color_set
},
edit: function() {
// TODO model access
if (!this.validate()) {
return;
}
var record = this.screen.current_record;
if (record) {
var win = new Sao.Window.Form(this.screen, function() {});
}
},
validate: function() {
this.view.set_value();
var record = this.screen.current_record;
if (record) {
var fields = this.screen.current_view.get_fields();
if (!record.validate(fields)) {
this.screen.display();
return false;
}
// TODO pre-validate
}
return true;
}
});
Sao.View.Form.Many2Many = Sao.class_(Sao.View.Form.Widget, {
class_: 'form-many2many',
init: function(field_name, model, attributes) {
Sao.View.Form.Many2Many._super.init.call(this, field_name, model,
attributes);
this.el = jQuery('<div/>', {
'class': this.class_
});
this.menu = jQuery('<div/>', {
'class': this.class_ + '-menu'
});
this.el.append(this.menu);
var label = jQuery('<span/>', {
text: attributes.string
});
this.menu.append(label);
this.entry = jQuery('<input/>', {
type: 'input'
});
this.entry.on('keyup', this.key_press.bind(this));
this.menu.append(this.entry);
// TODO completion
this.but_add = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-plus'
},
text: false
});
this.but_add.click(this.add.bind(this));
this.menu.append(this.but_add);
this.but_remove = jQuery('<button/>').button({
icons: {
primary: 'ui-icon-minus'
},
text: false
});
this.but_remove.click(this.remove.bind(this));
this.menu.append(this.but_remove);
this.content = jQuery('<div/>', {
'class': this.class_ + '-content'
});
this.el.append(this.content);
this.screen = new Sao.Screen(attributes.relation, {
mode: ['tree'],
view_ids: (attributes.view_ids || '').split(','),
views_preload: attributes.views || {},
row_activate: this.activate.bind(this)
});
this.prm = this.screen.switch_view('tree').done(function() {
this.content.append(this.screen.screen_container.el);
}.bind(this));
},
display: function(record, field) {
Sao.View.Form.Many2Many._super.display.call(this, record, field);
this.prm.done(function() {
if (!record) {
return;
}
if (field === undefined) {
this.screen.new_group();
this.screen.current_record = null;
this.screen.parent = true;
this.screen.display();
return;
}
var new_group = record.field_get_client(this.field_name);
if (new_group != this.screen.group) {
this.screen.set_group(new_group);
}
this.screen.display();
}.bind(this));
},
activate: function() {
this.edit();
},
add: function() {
var dom;
var domain = this.field().get_domain(this.record());
var context = this.field().get_context(this.record());
var value = this.entry.val();
if (value) {
dom = [['rec_name', 'ilike', '%' + value + '%']].concat(domain);
} else {
dom = domain;
}
var callback = function(result) {
if (!jQuery.isEmptyObject(result)) {
var ids = [];
var i, len;
for (i = 0, len = result.length; i < len; i++) {
ids.push(result[i][0]);
}
this.screen.group.load(ids, true);
this.screen.display();
}
this.entry.val('');
}.bind(this);
var model = new Sao.Model(this.attributes.relation);
var ids_prm = model.execute('search',
[dom, 0, Sao.config.limit, null], context);
ids_prm.done(function(ids) {
if (ids.length != 1) {
var win = new Sao.Window.Search(this.attributes.relation,
callback, {
sel_multi: true,
ids: ids,
context: context,
domain: domain,
view_ids: (this.attributes.view_ids ||
'').split(','),
views_preload: this.attributes.views || {},
new_: this.attributes.create
});
} else {
callback([[ids[0], null]]);
}
}.bind(this));
},
remove: function() {
this.screen.remove(false, true, false);
},
key_press: function(event_) {
var editable = true; // TODO compute editable
var activate_keys = [Sao.common.TAB_KEYCODE];
if (!this.wid_completion) {
activate_keys.push(Sao.common.RETURN_KEYCODE);
}
if (event_.which == Sao.common.F3_KEYCODE) {
this.add();
event_.preventDefault();
} else if ((jQuery.inArray(event_.which, activate_keys) > -1) &&
editable) {
if (this.entry.val()) {
this.add();
}
}
},
edit: function() {
if (this.screen.current_record) {
var callback = function(result) {
if (result) {
this.screen.current_record.save().done(function() {
this.screen.display();
}.bind(this));
} else {
this.screen.current_record.cancel();
}
};
var win = new Sao.Window.Form(this.screen,
callback.bind(this));
}
}
});
}());
+22 -9
View File
@@ -7,21 +7,27 @@
Sao.Window.Form = Sao.class_(Object, {
init: function(screen, callback, kwargs) {
kwargs = kwargs || {};
this.screen = screen;
this.screen.screen_container.alternate_view = true;
var view_type = kwargs.view_type || 'form';
var form_prm = jQuery.when();
var screen_views = [];
for (var i = 0, len = this.screen.views; i < len; i++) {
for (var i = 0, len = this.screen.views.length; i < len; i++) {
screen_views.push(this.screen.views[i].view_type);
}
if (screen_views.indexOf(view_type) == -1 &&
this.screen.view_to_load.indexOf(view_type) == -1) {
this.screen.add_view_id(null, view_type);
form_prm = this.screen.add_view_id(null, view_type);
}
var switch_prm = this.screen.switch_view(view_type);
switch_prm.done(function() {
if (kwargs.new_) {
this.screen.new_();
}
var switch_prm = form_prm.then(function() {
return this.screen.switch_view(view_type).done(function() {
if (kwargs.new_) {
this.screen.new_();
}
}.bind(this));
}.bind(this));
this.many = kwargs.many || 0;
this.domain = kwargs.domain || null;
@@ -65,7 +71,7 @@
buttons: buttons,
title: '' // this.screen.current_view
});
this.el.append(this.screen.screen_container.content_box);
this.el.append(this.screen.screen_container.alternate_viewport);
this.el.dialog('open');
this.screen.display();
}.bind(this));
@@ -126,13 +132,20 @@
closing_prm.done(function() {
this.callback(result);
this.el.dialog('destroy');
this.destroy();
}.bind(this));
},
destroy: function() {
this.screen.screen_container.alternate_view = false;
this.screen.screen_container.alternate_viewport.children()
.detach();
this.el.dialog('destroy');
}
});
Sao.Window.Search = Sao.class_(Object, {
init: function(model, callback, kwargs) {
kwargs = kwargs || {};
var views_preload = kwargs.views_preload || {};
this.model_name = model;
this.domain = kwargs.domain || [];