[MERGE] forward port of branch 7.0 up to revid 4067 chs@openerp.com-20131114142639-ng7wzfjwvvel2nhv
bzr revid: dle@openerp.com-20131112134311-h1vsux0ge17bsqkc bzr revid: chs@openerp.com-20131114134731-n324awyon0spq624 bzr revid: chs@openerp.com-20130823145204-xwpnlwg0gg2259f6 bzr revid: chs@openerp.com-20130906170157-e7m4pjskyi47q82o bzr revid: dle@openerp.com-20130909170408-wxgoduzggap6o4ng bzr revid: dle@openerp.com-20130919141212-ridtrvvfwvu6calr bzr revid: dle@openerp.com-20131018120136-fvoq337kgx74njsy bzr revid: dle@openerp.com-20131023103308-18pj2gqq3imrcir7 bzr revid: chs@openerp.com-20131030180528-hqsztaujjjqev8ky bzr revid: dle@openerp.com-20131106100128-mx8mnguvp321wick bzr revid: chs@openerp.com-20131115104909-3u3mu40g9xnler88
This commit is contained in:
commit
993bff902e
|
@ -741,9 +741,6 @@
|
|||
display: block;
|
||||
color: #4c4c4c;
|
||||
text-decoration: none;
|
||||
width: 200px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.openerp .oe_dropdown_menu > li > a:hover {
|
||||
text-decoration: none;
|
||||
|
@ -1455,7 +1452,7 @@
|
|||
display: table-row;
|
||||
height: inherit;
|
||||
}
|
||||
.openerp .oe_view_manager .oe_view_manager_view_kanban {
|
||||
.openerp .oe_view_manager .oe_view_manager_view_kanban:not(:empty) {
|
||||
height: inherit;
|
||||
}
|
||||
.openerp .oe_view_manager table.oe_view_manager_header {
|
||||
|
|
|
@ -632,9 +632,6 @@ $sheet-padding: 16px
|
|||
display: block
|
||||
color: #4c4c4c
|
||||
text-decoration: none
|
||||
width: 200px
|
||||
text-overflow: ellipsis
|
||||
overflow: hidden
|
||||
&:hover
|
||||
text-decoration: none
|
||||
.oe_dropdown_arrow:after
|
||||
|
@ -1171,7 +1168,7 @@ $sheet-padding: 16px
|
|||
.oe_view_manager_body
|
||||
display: table-row
|
||||
height: inherit
|
||||
.oe_view_manager_view_kanban
|
||||
.oe_view_manager_view_kanban:not(:empty)
|
||||
height: inherit
|
||||
|
||||
table.oe_view_manager_header
|
||||
|
|
|
@ -432,7 +432,7 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin,
|
|||
// TODO: reorder results to match ids list
|
||||
return this._model.call('read',
|
||||
[ids, fields || false],
|
||||
{context: this._model.context(options.context)});
|
||||
{context: this.get_context(options.context)});
|
||||
},
|
||||
/**
|
||||
* Read a slice of the records represented by this DataSet, based on its
|
||||
|
@ -724,7 +724,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
|
|||
});
|
||||
},
|
||||
get_domain: function (other_domain) {
|
||||
this._model.domain(other_domain);
|
||||
return this._model.domain(other_domain);
|
||||
},
|
||||
alter_ids: function (ids) {
|
||||
this._super(ids);
|
||||
|
@ -761,6 +761,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
this._super.apply(this, arguments);
|
||||
this.reset_ids([]);
|
||||
this.last_default_get = {};
|
||||
this.running_reads = [];
|
||||
},
|
||||
default_get: function(fields, options) {
|
||||
var self = this;
|
||||
|
@ -827,6 +828,9 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
this.to_write = [];
|
||||
this.cache = [];
|
||||
this.delete_all = false;
|
||||
_.each(_.clone(this.running_reads), function(el) {
|
||||
el.reject();
|
||||
});
|
||||
},
|
||||
read_ids: function (ids, fields, options) {
|
||||
var self = this;
|
||||
|
@ -842,7 +846,6 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
to_get.push(id);
|
||||
}
|
||||
});
|
||||
var completion = $.Deferred();
|
||||
var return_records = function() {
|
||||
var records = _.map(ids, function(id) {
|
||||
return _.extend({}, _.detect(self.cache, function(c) {return c.id === id;}).values, {"id": id});
|
||||
|
@ -877,10 +880,20 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
}, 0);
|
||||
});
|
||||
}
|
||||
completion.resolve(records);
|
||||
return $.when(records);
|
||||
};
|
||||
if(to_get.length > 0) {
|
||||
var rpc_promise = this._super(to_get, fields, options).done(function(records) {
|
||||
var def = $.Deferred();
|
||||
self.running_reads.push(def);
|
||||
def.always(function() {
|
||||
self.running_reads = _.without(self.running_reads, def);
|
||||
});
|
||||
this._super(to_get, fields, options).then(function() {
|
||||
def.resolve.apply(def, arguments);
|
||||
}, function() {
|
||||
def.reject.apply(def, arguments);
|
||||
});
|
||||
return def.then(function(records) {
|
||||
_.each(records, function(record, index) {
|
||||
var id = to_get[index];
|
||||
var cached = _.detect(self.cache, function(x) {return x.id === id;});
|
||||
|
@ -891,13 +904,11 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
|
|||
cached.values = _.defaults(_.clone(cached.values), record);
|
||||
}
|
||||
});
|
||||
return_records();
|
||||
return return_records();
|
||||
});
|
||||
$.when(rpc_promise).fail(function() {completion.reject();});
|
||||
} else {
|
||||
return_records();
|
||||
return return_records();
|
||||
}
|
||||
return completion.promise();
|
||||
},
|
||||
/**
|
||||
* Invalidates caching of a record in the dataset to ensure the next read
|
||||
|
|
|
@ -1540,7 +1540,7 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
|
|||
context: context
|
||||
}).then(function (results) {
|
||||
if (_.isEmpty(results)) { return null; }
|
||||
return [{label: _.escape(self.attrs.string)}].concat(
|
||||
return [{label: self.attrs.string}].concat(
|
||||
_(results).map(function (result) {
|
||||
return {
|
||||
label: _.escape(result[1]),
|
||||
|
@ -1726,7 +1726,10 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
|
|||
var $name = this.$('input:first');
|
||||
var private_filter = !this.$('#oe_searchview_custom_public').prop('checked');
|
||||
var set_as_default = this.$('#oe_searchview_custom_default').prop('checked');
|
||||
|
||||
if (_.isEmpty($name.val())){
|
||||
this.do_warn(_t("Error"), _t("Filter name is required."));
|
||||
return false;
|
||||
}
|
||||
var search = this.view.build_search_data();
|
||||
instance.web.pyeval.eval_domains_and_contexts({
|
||||
domains: search.domains,
|
||||
|
|
|
@ -3027,7 +3027,7 @@ instance.web.form.CompletionFieldMixin = {
|
|||
values.push({
|
||||
label: _t("Search More..."),
|
||||
action: function() {
|
||||
dataset.name_search(search_val, self.build_domain(), 'ilike', false).done(function(data) {
|
||||
dataset.name_search(search_val, self.build_domain(), 'ilike', 160).done(function(data) {
|
||||
self._search_create_popup("search", data);
|
||||
});
|
||||
},
|
||||
|
@ -3649,7 +3649,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
_.extend(view.options, {
|
||||
addable: null,
|
||||
selectable: self.multi_selection,
|
||||
sortable: false,
|
||||
sortable: true,
|
||||
import_enabled: false,
|
||||
deletable: true
|
||||
});
|
||||
|
@ -4044,7 +4044,13 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
else
|
||||
return $.when();
|
||||
}).done(function () {
|
||||
self.handle_button(name, id, callback);
|
||||
if (!self.o2m.options.reload_on_button) {
|
||||
self.handle_button(name, id, callback);
|
||||
}else {
|
||||
self.handle_button(name, id, function(){
|
||||
self.o2m.view.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -496,17 +496,21 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
*
|
||||
* @returns {$.Deferred} promise to content reloading
|
||||
*/
|
||||
reload_content: function () {
|
||||
reload_content: synchronized(function () {
|
||||
var self = this;
|
||||
self.$el.find('.oe_list_record_selector').prop('checked', false);
|
||||
this.records.reset();
|
||||
var reloaded = $.Deferred();
|
||||
this.$el.find('.oe_list_content').append(
|
||||
this.groups.render(function () {
|
||||
if (self.dataset.index == null && self.records.length ||
|
||||
self.dataset.index >= self.records.length) {
|
||||
if (self.dataset.index == null) {
|
||||
if (self.records.length) {
|
||||
self.dataset.index = 0;
|
||||
}
|
||||
} else if (self.dataset.index >= self.records.length) {
|
||||
self.dataset.index = 0;
|
||||
}
|
||||
|
||||
self.compute_aggregates();
|
||||
reloaded.resolve();
|
||||
}));
|
||||
|
@ -515,7 +519,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
limit: this._limit
|
||||
});
|
||||
return reloaded.promise();
|
||||
},
|
||||
}),
|
||||
reload: function () {
|
||||
return this.reload_content();
|
||||
},
|
||||
|
@ -1328,6 +1332,9 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
|
|||
.removeClass('ui-icon-triangle-1-s')
|
||||
.addClass('ui-icon-triangle-1-e');
|
||||
child.close();
|
||||
// force recompute the selection as closing group reset properties
|
||||
var selection = self.get_selection();
|
||||
$(self).trigger('selected', [selection.ids, this.records]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1338,22 +1345,29 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
|
|||
if (group.grouped_on) {
|
||||
var row_data = {};
|
||||
row_data[group.grouped_on] = group;
|
||||
var group_label = _t("Undefined");
|
||||
var group_column = _(self.columns).detect(function (column) {
|
||||
return column.id === group.grouped_on; });
|
||||
if (! group_column) {
|
||||
throw new Error(_.str.sprintf(
|
||||
_t("Grouping on field '%s' is not possible because that field does not appear in the list view."),
|
||||
group.grouped_on));
|
||||
}
|
||||
var group_label;
|
||||
try {
|
||||
group_label = group_column.format(row_data, {
|
||||
value_if_empty: _t("Undefined"),
|
||||
process_modifiers: false
|
||||
});
|
||||
} catch (e) {
|
||||
group_label = _.str.escapeHTML(row_data[group_column.id].value);
|
||||
if (group_column) {
|
||||
try {
|
||||
group_label = group_column.format(row_data, {
|
||||
value_if_empty: _t("Undefined"),
|
||||
process_modifiers: false
|
||||
});
|
||||
} catch (e) {
|
||||
group_label = _.str.escapeHTML(row_data[group_column.id].value);
|
||||
}
|
||||
} else {
|
||||
group_label = group.value;
|
||||
if (group_label instanceof Array) {
|
||||
group_label = group_label[1];
|
||||
}
|
||||
if (group_label === false) {
|
||||
group_label = _t('Undefined');
|
||||
}
|
||||
group_label = _.str.escapeHTML(group_label);
|
||||
}
|
||||
|
||||
// group_label is html-clean (through format or explicit
|
||||
// escaping if format failed), can inject straight into HTML
|
||||
$group_column.html(_.str.sprintf(_t("%s (%d)"),
|
||||
|
@ -1425,14 +1439,13 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
|
|||
|
||||
var view = this.view,
|
||||
limit = view.limit(),
|
||||
d = new $.Deferred(),
|
||||
page = this.datagroup.openable ? this.page : view.page;
|
||||
|
||||
var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name');
|
||||
var options = { offset: page * limit, limit: limit, context: {bin_size: true} };
|
||||
//TODO xmo: investigate why we need to put the setTimeout
|
||||
$.async_when().done(function() {
|
||||
dataset.read_slice(fields, options).done(function (records) {
|
||||
return $.async_when().then(function() {
|
||||
return dataset.read_slice(fields, options).then(function (records) {
|
||||
// FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently
|
||||
if (self.records.length) {
|
||||
self.records.reset(null, {silent: true});
|
||||
|
@ -1464,13 +1477,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
|
|||
|
||||
self.records.add(records, {silent: true});
|
||||
list.render();
|
||||
d.resolve(list);
|
||||
if (_.isEmpty(records)) {
|
||||
view.no_result();
|
||||
}
|
||||
return list;
|
||||
});
|
||||
});
|
||||
return d.promise();
|
||||
},
|
||||
setup_resequence_rows: function (list, dataset) {
|
||||
// drag and drop enabled if list is not sorted and there is a
|
||||
|
@ -1550,11 +1562,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
|
|||
self.render_groups(groups));
|
||||
if (post_render) { post_render(); }
|
||||
}, function (dataset) {
|
||||
self.render_dataset(dataset).done(function (list) {
|
||||
self.render_dataset(dataset).then(function (list) {
|
||||
self.children[null] = list;
|
||||
self.elements =
|
||||
[list.$current.replaceAll($el)[0]];
|
||||
self.setup_resequence_rows(list, dataset);
|
||||
}).always(function() {
|
||||
if (post_render) { post_render(); }
|
||||
});
|
||||
});
|
||||
|
@ -1597,6 +1610,22 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Serializes concurrent calls to this asynchronous method. The method must
|
||||
* return a deferred or promise.
|
||||
*
|
||||
* Current-implementation is class-serialized (the mutex is common to all
|
||||
* instances of the list view). Can be switched to instance-serialized if
|
||||
* having concurrent list views becomes possible and common.
|
||||
*/
|
||||
function synchronized(fn) {
|
||||
var fn_mutex = new $.Mutex();
|
||||
return function () {
|
||||
var args = _.toArray(arguments);
|
||||
args.unshift(this);
|
||||
return fn_mutex.exec(fn.bind.apply(fn, args));
|
||||
};
|
||||
}
|
||||
var DataGroup = instance.web.Class.extend({
|
||||
init: function(parent, model, domain, context, group_by, level) {
|
||||
this.model = new instance.web.Model(model, context, domain);
|
||||
|
@ -1608,6 +1637,10 @@ var DataGroup = instance.web.Class.extend({
|
|||
},
|
||||
list: function (fields, ifGroups, ifRecords) {
|
||||
var self = this;
|
||||
if (!_.isEmpty(this.group_by)) {
|
||||
// ensure group_by fields are read.
|
||||
fields = _.unique((fields || []).concat(this.group_by));
|
||||
}
|
||||
var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
|
||||
$.when(query).done(function (querygroups) {
|
||||
// leaf node
|
||||
|
|
|
@ -163,12 +163,13 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
|
|||
var is_loaded = 0,
|
||||
$this = $(this),
|
||||
record_id = $this.data('id'),
|
||||
row_parent_id = $this.data('row-parent-id'),
|
||||
record = self.records[record_id],
|
||||
children_ids = record[self.children_field];
|
||||
|
||||
_(children_ids).each(function(childid) {
|
||||
if (self.$el.find('#treerow_' + childid).length) {
|
||||
if (self.$el.find('#treerow_' + childid).is(':hidden')) {
|
||||
if (self.$el.find('[id=treerow_' + childid + '][data-row-parent-id='+ record_id +']').length ) {
|
||||
if (self.$el.find('[id=treerow_' + childid + '][data-row-parent-id='+ record_id +']').is(':hidden')) {
|
||||
is_loaded = -1;
|
||||
} else {
|
||||
is_loaded++;
|
||||
|
@ -180,7 +181,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
|
|||
self.getdata(record_id, children_ids);
|
||||
}
|
||||
} else {
|
||||
self.showcontent(record_id, is_loaded < 0);
|
||||
self.showcontent($this, record_id, is_loaded < 0);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -192,7 +193,6 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
|
|||
_(records).each(function (record) {
|
||||
self.records[record.id] = record;
|
||||
});
|
||||
|
||||
var $curr_node = self.$el.find('#treerow_' + id);
|
||||
var children_rows = QWeb.render('TreeView.rows', {
|
||||
'records': records,
|
||||
|
@ -201,9 +201,9 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
|
|||
'fields': self.fields,
|
||||
'level': $curr_node.data('level') || 0,
|
||||
'render': instance.web.format_value,
|
||||
'color_for': self.color_for
|
||||
'color_for': self.color_for,
|
||||
'row_parent_id': id
|
||||
});
|
||||
|
||||
if ($curr_node.length) {
|
||||
$curr_node.addClass('oe_open');
|
||||
$curr_node.after(children_rows);
|
||||
|
@ -243,14 +243,13 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
|
|||
},
|
||||
|
||||
// show & hide the contents
|
||||
showcontent: function (record_id, show) {
|
||||
this.$el.find('#treerow_' + record_id)
|
||||
.toggleClass('oe_open', show);
|
||||
|
||||
showcontent: function (curnode,record_id, show) {
|
||||
curnode.parent('tr').toggleClass('oe_open', show);
|
||||
_(this.records[record_id][this.children_field]).each(function (child_id) {
|
||||
var $child_row = this.$el.find('#treerow_' + child_id);
|
||||
var $child_row = this.$el.find('[id=treerow_' + child_id + '][data-row-parent-id='+ curnode.data('id') +']');
|
||||
if ($child_row.hasClass('oe_open')) {
|
||||
this.showcontent(child_id, false);
|
||||
$child_row.toggleClass('oe_open',show);
|
||||
this.showcontent($child_row, child_id, false);
|
||||
}
|
||||
$child_row.toggle(show);
|
||||
}, this);
|
||||
|
|
|
@ -684,7 +684,8 @@
|
|||
<tr t-name="TreeView.rows"
|
||||
t-foreach="records" t-as="record"
|
||||
t-att-id="'treerow_' + record.id"
|
||||
t-att-data-id="record.id" t-att-data-level="level + 1">
|
||||
t-att-data-id="record.id" t-att-data-level="level + 1"
|
||||
t-att-data-row-parent-id="row_parent_id">
|
||||
<t t-set="children" t-value="record[children_field]"/>
|
||||
<t t-set="class" t-value="children and children.length ? 'treeview-tr' : 'treeview-td'"/>
|
||||
<t t-set="rank" t-value="'oe-treeview-first'"/>
|
||||
|
@ -1651,8 +1652,8 @@
|
|||
<ul class="oe_searchview_custom_list"/>
|
||||
<div class="oe_searchview_custom">
|
||||
<h4>Save current filter</h4>
|
||||
<form>
|
||||
<p><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
|
||||
<form class="oe_form">
|
||||
<p class="oe_form_required"><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
|
||||
<p>
|
||||
<input id="oe_searchview_custom_public" type="checkbox"/>
|
||||
<label for="oe_searchview_custom_public">Share with all users</label>
|
||||
|
|
|
@ -1293,7 +1293,7 @@ openerp.testing.section('search.filters.saved', {
|
|||
return view.appendTo($fix)
|
||||
.then(function () {
|
||||
$fix.find('.oe_searchview_custom input#oe_searchview_custom_input')
|
||||
.text("filter name")
|
||||
.val("filter name")
|
||||
.end()
|
||||
.find('.oe_searchview_custom button').click();
|
||||
return done.promise();
|
||||
|
|
|
@ -566,8 +566,8 @@
|
|||
display: inline-block !important; }
|
||||
.openerp_ie .oe_kanban_view .oe_kanban_group_title_vertical {
|
||||
-ms-writing-mode: lr-tb !important;
|
||||
background: #f0eeee;
|
||||
top: -5px !important; }
|
||||
background: #f0eeee;}
|
||||
|
||||
.openerp_ie .oe_kanban_view.oe_kanban_grouped .oe_kanban_group_header {
|
||||
height: 1%; }
|
||||
|
||||
|
|
|
@ -584,7 +584,6 @@
|
|||
.oe_kanban_group_title_vertical
|
||||
-ms-writing-mode: lr-tb !important
|
||||
background: rgb(240, 238, 238)
|
||||
top: -5px !important
|
||||
&.oe_kanban_grouped
|
||||
.oe_kanban_group_header
|
||||
height: 1%
|
||||
|
|
|
@ -235,7 +235,11 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
|
|||
self.$buttons.find('.oe_alternative').toggle(self.grouped_by_m2o);
|
||||
self.$el.toggleClass('oe_kanban_grouped_by_m2o', self.grouped_by_m2o);
|
||||
var grouping_fields = self.group_by ? [self.group_by].concat(_.keys(self.aggregates)) : undefined;
|
||||
var grouping = new instance.web.Model(self.dataset.model, context, domain).query().group_by(grouping_fields);
|
||||
if (!_.isEmpty(grouping_fields)) {
|
||||
// ensure group_by fields are read.
|
||||
self.fields_keys = _.unique(self.fields_keys.concat(grouping_fields));
|
||||
}
|
||||
var grouping = new instance.web.Model(self.dataset.model, context, domain).query(self.fields_keys).group_by(grouping_fields);
|
||||
return self.alive($.when(grouping)).done(function(groups) {
|
||||
self.remove_no_result();
|
||||
if (groups) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from openerp.osv import orm, fields
|
||||
|
||||
class TestObject(orm.Model):
|
||||
class TestObject(orm.TransientModel):
|
||||
_name = 'web_tests_demo.model'
|
||||
|
||||
_columns = {
|
||||
|
|
|
@ -253,7 +253,7 @@ class ir_fields_converter(orm.Model):
|
|||
if not isinstance(selection, (tuple, list)):
|
||||
# FIXME: Don't pass context to avoid translations?
|
||||
# Or just copy context & remove lang?
|
||||
selection = selection(model, cr, uid)
|
||||
selection = selection(model, cr, uid, context=None)
|
||||
for item, label in selection:
|
||||
labels = self._get_translations(
|
||||
cr, uid, ('selection', 'model', 'code'), label, context=context)
|
||||
|
|
|
@ -304,5 +304,47 @@ class test_partner_recursion(common.TransactionCase):
|
|||
cr, uid, p1, p2, p3 = self.cr, self.uid, self.p1, self.p2, self.p3
|
||||
self.assertTrue(self.res_partner.write(cr, uid, [p1,p2,p3], {'phone': '123456'}))
|
||||
|
||||
class test_translation(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(test_translation, self).setUp()
|
||||
self.res_category = self.registry('res.partner.category')
|
||||
self.ir_translation = self.registry('ir.translation')
|
||||
cr, uid = self.cr, self.uid
|
||||
self.registry('ir.translation').load(cr, ['base'], ['fr_BE'])
|
||||
self.cat_id = self.res_category.create(cr, uid, {'name': 'Customers'})
|
||||
self.ir_translation.create(cr, uid, {'name': 'res.partner.category,name', 'module':'base',
|
||||
'value': 'Clients', 'res_id': self.cat_id, 'lang':'fr_BE', 'state':'translated', 'type': 'model'})
|
||||
|
||||
def test_101_create_translated_record(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
|
||||
no_context_cat = self.res_category.browse(cr, uid, self.cat_id)
|
||||
self.assertEqual(no_context_cat.name, 'Customers', "Error in basic name_get")
|
||||
|
||||
fr_context_cat = self.res_category.browse(cr, uid, self.cat_id, context={'lang':'fr_BE'})
|
||||
self.assertEqual(fr_context_cat.name, 'Clients', "Translation not found")
|
||||
|
||||
def test_102_duplicate_record(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
self.new_cat_id = self.res_category.copy(cr, uid, self.cat_id, context={'lang':'fr_BE'})
|
||||
|
||||
no_context_cat = self.res_category.browse(cr, uid, self.new_cat_id)
|
||||
self.assertEqual(no_context_cat.name, 'Customers', "Duplication did not set untranslated value")
|
||||
|
||||
fr_context_cat = self.res_category.browse(cr, uid, self.new_cat_id, context={'lang':'fr_BE'})
|
||||
self.assertEqual(fr_context_cat.name, 'Clients', "Did not found translation for initial value")
|
||||
|
||||
def test_103_duplicate_record_fr(self):
|
||||
cr, uid = self.cr, self.uid
|
||||
self.new_fr_cat_id = self.res_category.copy(cr, uid, self.cat_id, default={'name': 'Clients (copie)'}, context={'lang':'fr_BE'})
|
||||
|
||||
no_context_cat = self.res_category.browse(cr, uid, self.new_fr_cat_id)
|
||||
self.assertEqual(no_context_cat.name, 'Clients (copie)', "Duplication with default value not applied")
|
||||
|
||||
fr_context_cat = self.res_category.browse(cr, uid, self.new_fr_cat_id, context={'lang':'fr_BE'})
|
||||
self.assertEqual(fr_context_cat.name, 'Clients', "Did not found translation for initial value")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
||||
|
|
|
@ -5016,7 +5016,6 @@ class BaseModel(object):
|
|||
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
|
||||
fields = self.fields_get(cr, uid, context=context)
|
||||
|
||||
translation_records = []
|
||||
for field_name, field_def in fields.items():
|
||||
# we must recursively copy the translations for o2o and o2m
|
||||
if field_def['type'] == 'one2many':
|
||||
|
@ -5030,22 +5029,31 @@ class BaseModel(object):
|
|||
target_obj.copy_translations(cr, uid, old_child, new_child, context=context)
|
||||
# and for translatable fields we keep them for copy
|
||||
elif field_def.get('translate'):
|
||||
trans_name = ''
|
||||
|
||||
if field_name in self._columns:
|
||||
trans_name = self._name + "," + field_name
|
||||
res_id = new_id
|
||||
|
||||
elif field_name in self._inherit_fields:
|
||||
trans_name = self._inherit_fields[field_name][0] + "," + field_name
|
||||
if trans_name:
|
||||
trans_ids = trans_obj.search(cr, uid, [
|
||||
('name', '=', trans_name),
|
||||
('res_id', '=', old_id)
|
||||
])
|
||||
translation_records.extend(trans_obj.read(cr, uid, trans_ids, context=context))
|
||||
# get the id of the parent record to set the translation
|
||||
inherit_field_name = self._inherit_fields[field_name][1]
|
||||
res_id = self.read(cr, uid, [new_id], [inherit_field_name], context=context)[0][inherit_field_name][0]
|
||||
|
||||
for record in translation_records:
|
||||
del record['id']
|
||||
record['res_id'] = new_id
|
||||
trans_obj.create(cr, uid, record, context=context)
|
||||
else:
|
||||
continue
|
||||
|
||||
trans_ids = trans_obj.search(cr, uid, [
|
||||
('name', '=', trans_name),
|
||||
('res_id', '=', old_id)
|
||||
])
|
||||
records = trans_obj.read(cr, uid, trans_ids, context=context)
|
||||
for record in records:
|
||||
del record['id']
|
||||
# remove source to avoid triggering _set_src
|
||||
del record['source']
|
||||
record.update({'res_id': res_id})
|
||||
trans_obj.create(cr, uid, record, context=context)
|
||||
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
|
|
|
@ -631,6 +631,18 @@ class _rml_Illustration(platypus.flowables.Flowable):
|
|||
drw = _rml_draw(self.localcontext ,self.node,self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title)
|
||||
drw.render(self.canv, None)
|
||||
|
||||
# Workaround for issue #15: https://bitbucket.org/rptlab/reportlab/issue/15/infinite-pages-produced-when-splitting
|
||||
original_pto_split = platypus.flowables.PTOContainer.split
|
||||
def split(self, availWidth, availHeight):
|
||||
res = original_pto_split(self, availWidth, availHeight)
|
||||
if len(res) > 2 and len(self._content) > 0:
|
||||
header = self._content[0]._ptoinfo.header
|
||||
trailer = self._content[0]._ptoinfo.trailer
|
||||
if isinstance(res[-2], platypus.flowables.UseUpSpace) and len(header + trailer) == len(res[:-2]):
|
||||
return []
|
||||
return res
|
||||
platypus.flowables.PTOContainer.split = split
|
||||
|
||||
class _rml_flowable(object):
|
||||
def __init__(self, doc, localcontext, images=None, path='.', title=None, canvas=None):
|
||||
if images is None:
|
||||
|
@ -1012,11 +1024,16 @@ class _rml_template(object):
|
|||
# Reset Page Number with new story tag
|
||||
fis.append(PageReset())
|
||||
story_cnt += 1
|
||||
if self.localcontext and self.localcontext.get('internal_header',False):
|
||||
self.doc_tmpl.afterFlowable(fis)
|
||||
self.doc_tmpl.build(fis,canvasmaker=NumberedCanvas)
|
||||
else:
|
||||
self.doc_tmpl.build(fis)
|
||||
try:
|
||||
if self.localcontext and self.localcontext.get('internal_header',False):
|
||||
self.doc_tmpl.afterFlowable(fis)
|
||||
self.doc_tmpl.build(fis,canvasmaker=NumberedCanvas)
|
||||
else:
|
||||
self.doc_tmpl.build(fis)
|
||||
except platypus.doctemplate.LayoutError, e:
|
||||
e.name = 'Print Error'
|
||||
e.value = 'The document you are trying to print contains a table row that does not fit on one page. Please try to split it in smaller rows or contact your administrator.'
|
||||
raise
|
||||
|
||||
def parseNode(rml, localcontext=None, fout=None, images=None, path='.', title=None):
|
||||
node = etree.XML(rml)
|
||||
|
|
Loading…
Reference in New Issue