_order = "id desc"
def create_from_ui(self, cr, uid, orders, context=None):
- #_logger.info("orders: %r", orders)
+
+ # Keep only new orders
+ submitted_references = [o['data']['name'] for o in orders]
+ existing_orders = self.search_read(cr, uid, domain=[('pos_reference', 'in', submitted_references)], fields=['pos_reference'], context=context)
+ existing_references = set([o['pos_reference'] for o in existing_orders])
+ orders_to_save = [o for o in orders if o['data']['name'] not in existing_references]
+
order_ids = []
- for tmp_order in orders:
+ for tmp_order in orders_to_save:
to_invoice = tmp_order['to_invoice']
order = tmp_order['data']
-
order_id = self.create(cr, uid, {
'name': order['name'],
'user_id': order['user_id'] or False,
// it is therefore important to only call this method from inside a mutex
// this method returns a deferred indicating wether the sending was successful or not
// there is a timeout parameter which is set to 2 seconds by default.
- _flush_order: function(order_id, options){
- var self = this;
- options = options || {};
- timeout = typeof options.timeout === 'number' ? options.timeout : 7500;
-
- this.set('synch',{state:'connecting', pending: this.get('synch').pending});
-
- var order = this.db.get_order(order_id);
- order.to_invoice = options.to_invoice || false;
-
- if(!order){
- // flushing a non existing order always fails
- return (new $.Deferred()).reject();
- }
-
- // we try to send the order. shadow prevents a spinner if it takes too long. (unless we are sending an invoice,
- // then we want to notify the user that we are waiting on something )
- var rpc = (new instance.web.Model('pos.order')).call('create_from_ui',[[order]],undefined,{shadow: !options.to_invoice, timeout:timeout});
-
- rpc.fail(function(unused,event){
- // prevent an error popup creation by the rpc failure
- // we want the failure to be silent as we send the orders in the background
- event.preventDefault();
- console.error('Failed to send order:',order);
- });
-
- rpc.done(function(){
- self.db.remove_order(order_id);
- var pending = self.db.get_orders().length;
- self.set('synch',{state: pending ? 'connecting' : 'connected', pending:pending});
- });
-
- return rpc;
+ _flush_order: function( order_id, options) {
+ return this._flush_all_orders([this.db.get_order(order_id)], options);
},
// attempts to send all the locally stored orders. As with _flush_order, it should only be
// called from within a mutex.
// this method returns a deferred that always succeeds when all orders have been tried to be sent,
// even if none of them could actually be sent.
- _flush_all_orders: function(){
+ _flush_all_orders: function () {
var self = this;
- var orders = this.db.get_orders();
- var tried_all = new $.Deferred();
-
- function rec_flush(index){
- if(index < orders.length){
- self._flush_order(orders[index].id).always(function(){
- rec_flush(index+1);
- })
- }else{
- tried_all.resolve();
- }
+ self.set('synch', {
+ state: 'connecting',
+ pending: self.get('synch').pending
+ });
+ return self._save_to_server(self.db.get_orders()).done(function () {
+ var pending = self.db.get_orders().length;
+ self.set('synch', {
+ state: pending ? 'connecting' : 'connected',
+ pending: pending
+ });
+ });
+ },
+
+ // send an array of orders to the server
+ // available options:
+ // - timeout: timeout for the rpc call in ms
+ _save_to_server: function (orders, options) {
+ if (!orders || !orders.length) {
+ var result = $.Deferred();
+ result.resolve();
+ return result;
}
- rec_flush(0);
+
+ options = options || {};
+
+ var self = this;
+ var timeout = typeof options.timeout === 'number' ? options.timeout : 7500;
- return tried_all;
+ // we try to send the order. shadow prevents a spinner if it takes too long. (unless we are sending an invoice,
+ // then we want to notify the user that we are waiting on something )
+ var posOrderModel = new instance.web.Model('pos.order');
+ return posOrderModel.call('create_from_ui',
+ [_.map(orders, function (order) {
+ order.to_invoice = options.to_invoice || false;
+ return order;
+ })],
+ undefined,
+ {
+ shadow: !options.to_invoice,
+ timeout: timeout
+ }
+ ).then(function () {
+ _.each(orders, function (order) {
+ self.db.remove_order(order.id);
+ });
+ }).fail(function (unused, event){
+ // prevent an error popup creation by the rpc failure
+ // we want the failure to be silent as we send the orders in the background
+ event.preventDefault();
+ console.error('Failed to send orders:', orders);
+ });
},
scan_product: function(parsed_code){
model, map(operator.itemgetter('name'), export_fields_list))
return [
- {'name': field['name'], 'label': fields_data[field['name']]}
- for field in export_fields_list
+ {'name': field_name, 'label': fields_data[field_name]}
+ for field_name in fields_data.keys()
]
def fields_info(self, model, export_fields):
-all: *.css
-%.css: %.sass
- sass -t expanded --compass --unix-newlines $< $@
-watch:
- sass -t expanded --compass --unix-newlines --watch .:.
+sass:
+ sass -t expanded --compass --unix-newlines --watch base.sass:base.css&
\ No newline at end of file
.openerp td {
vertical-align: top;
}
-.openerp .oe_title {
- width: 50%;
- float: left;
-}
-.openerp .oe_title:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden;
-}
-.openerp .oe_form_group {
- clear: both;
-}
.openerp .zebra tbody tr:nth-child(odd) td {
background-color: #f0f0fa;
background-color: #efeff8;
.openerp .oe_about .oe_bottom a {
color: #eeeeee;
}
-.openerp a.oe_form_uri:hover {
+.openerp .oe_form_uri {
+ color: #7c7bad;
+}
+.openerp .oe_form_uri:hover {
text-decoration: underline;
}
.openerp .oe_application {
border-radius: 0 3px 3px 0;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value {
+ height: 18px;
padding: 0 4px;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category {
}
.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button {
margin: 3px 2px 1px;
+ float: left;
}
.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button:first-child {
margin-left: 6px;
}
.openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span {
font-size: 80%;
- color: darkgreen;
+ color: darkGreen;
vertical-align: top;
position: relative;
top: -4px;
font-size: 95%;
line-height: 1.2em;
}
+.openerp .oe_debug_view_log label {
+ display: block;
+ width: 49%;
+ text-align: right;
+ float: left;
+ font-weight: bold;
+ color: #000099;
+}
+.openerp .oe_debug_view_log span {
+ display: block;
+ width: 49%;
+ float: right;
+ color: #333333;
+}
.openerp .navbar {
min-height: 32px;
margin-bottom: 0px;
border: none;
z-index: 1;
- position: static;
background-color: #414141;
background-color: #454343;
background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626));
border: none;
padding: 10px 0 3px 0;
}
+.openerp .jqstooltip {
+ height: auto !important;
+ width: auto !important;
+}
.openerp h5 {
font-weight: bold;
font-size: smaller;
margin: 3px 3px 0 !important;
}
-.jqstooltip {
- height: auto !important;
- width: auto !important;
- padding: 0;
-}
-
@-moz-document url-prefix() {
.openerp .oe_searchview .oe_searchview_search {
top: -1px;
vertical-align: middle
td
vertical-align: top
- .oe_title
- width: 50%
- float: left
- .oe_title:after
- content: "."
- display: block
- height: 0
- clear: both
- visibility: hidden
- .oe_form_group
- clear: both
.zebra tbody tr:nth-child(odd) td
background-color: #f0f0fa
@include vertical-gradient(#f0f0fa, #eeeef6)
color: #eee
// }}}
// ActionManager {{{
- a.oe_form_uri:hover
- text-decoration: underline
+ .oe_form_uri
+ color: $link-color
+ &:hover
+ text-decoration: underline
.oe_application
width: 100%
height: 100%
background: $tag-bg-light
@include radius(0 3px 3px 0)
.oe_facet_category, .oe_facet_value
+ height: 18px
padding: 0 4px
.oe_facet_category
color: white
float: right
.oe_button
margin: 3px 2px 1px
+ float: left
&:first-child
margin-left: 6px
.oe_debug_view_log
font-size: 95%
line-height: 1.2em
+ .oe_debug_view_log label
+ display: block
+ width: 49%
+ text-align: right
+ float: left
+ font-weight: bold
+ color: #009
+ .oe_debug_view_log span
+ display: block
+ width: 49%
+ float: right
+ color: #333
// }}}
// Bootstrap HACKS {{{
.navbar
margin-bottom: 0px
border: none
z-index: 1
- position: static
background-color: #414141
@include vertical-gradient(#646060, #262626)
.navbar-default
border: none
padding: 10px 0 3px 0
-
+ // Customize for kanban tooltip
+ .jqstooltip
+ height: auto !important
+ width: auto !important
// Customize for chatter
h5
.oe_msg_subtype_check
margin: 3px 3px 0 !important
// }}}
-// Customize for kanban tooltip
-.jqstooltip
- height: auto !important
- width: auto !important
- padding: 0
-
+
@-moz-document url-prefix()
.openerp
.oe_searchview .oe_searchview_search
var state = $.bbq.getState(true);
if (_.isEmpty(state) || state.action == "login") {
self.menu.has_been_loaded.done(function() {
- new instance.web.Model("res.users").call("read", [self.session.uid, ["action_id"]]).done(function(data) {
- var first_menu_id = self.menu.$el.find("a:first").data("menu");
- if(first_menu_id)
- self.menu.menu_click(first_menu_id);
-
- if(data.action_id) {
- self.action_manager.do_action(data.action_id[0]);
- }
- });
+ var first_menu_id = self.menu.$el.find("a:first").data("menu");
+ if(first_menu_id) {
+ self.menu.menu_click(first_menu_id);
+ }
});
} else {
$(window).trigger('hashchange');
* @returns {$.Deferred}
*/
read_ids: function (ids, fields, options) {
- if (_.isEmpty(ids))
- return $.Deferred().resolve([]);
-
options = options || {};
// TODO: reorder results to match ids list
return this._model.call('read',
});
var return_records = function() {
var records = _.map(ids, function(id) {
- var c = _.find(self.cache, function(c) {return c.id === id;});
- return _.isUndefined(c) ? c : _.extend({}, c.values, {"id": id});
+ return _.extend({}, _.detect(self.cache, function(c) {return c.id === id;}).values, {"id": id});
});
if (self.debug_mode) {
if (_.include(records, undefined)) {
case 'selection': case 'statusbar':
// Each choice is [value, label]
if(_.isArray(value)) {
- return value[1];
+ value = value[0];
}
var result = _(descriptor.selection).detect(function (choice) {
return choice[0] === value;
context: this.dataset.get_context(),
});
- this.alive($.when(load_view)).then(function (r) {
+ $.when(load_view).then(function (r) {
return self.search_view_loaded(r);
}).fail(function () {
self.ready.reject.apply(null, arguments);
open_defaults_dialog: function () {
var self = this;
var display = function (field, value) {
- if (!value) { return value; }
if (field instanceof instance.web.form.FieldSelection) {
- return _(field.get('values')).find(function (option) {
+ return _(field.values).find(function (option) {
return option[0] === value;
})[1];
} else if (field instanceof instance.web.form.FieldMany2One) {
// quick create
var raw_result = _(data.result).map(function(x) {return x[1];});
if (search_val.length > 0 && !_.include(raw_result, search_val) &&
- ! (self.options && (self.options.no_create || self.options.no_quick_create))) {
+ ! (self.options && self.options.no_quick_create)) {
values.push({
label: _.str.sprintf(_t('Create "<strong>%s</strong>"'),
$('<span />').text(search_val).html()),
});
}
// create...
- if (!(self.options && self.options.no_create)){
- values.push({
- label: _t("Create and Edit..."),
- action: function() {
- self._search_create_popup("form", undefined, self._create_context(search_val));
- },
- classname: 'oe_m2o_dropdown_option'
- });
- }
+ values.push({
+ label: _t("Create and Edit..."),
+ action: function() {
+ self._search_create_popup("form", undefined, self._create_context(search_val));
+ },
+ classname: 'oe_m2o_dropdown_option'
+ });
return values;
});
.on('blurred', null, function () {self.trigger('blurred');});
this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: {
- name: 'Referenced Document',
+ name: 'm2o',
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.m2o.on("change:value", this, this.data_changed);
function synchronized(fn) {
var fn_mutex = new $.Mutex();
return function () {
- var obj = this;
var args = _.toArray(arguments);
- return fn_mutex.exec(function () {
- if (obj.isDestroyed()) { return $.when(); }
- return fn.apply(obj, args)
- });
+ args.unshift(this);
+ return fn_mutex.exec(fn.bind.apply(fn, args));
};
}
var DataGroup = instance.web.Class.extend({
action_views_ids : views_ids
}, self.flags, self.flags[view.view_type] || {}, view.options || {})
});
-
views_ids[view.view_type] = view.view_id;
});
if (this.flags.views_switcher === false) {
}
// If no default view defined, switch to the first one in sequence
var default_view = this.flags.default_view || this.views_src[0].view_type;
-
-
- return this.switch_mode(default_view, null, this.flags[default_view] && this.flags[default_view].options);
-
-
+ return this.switch_mode(default_view);
},
switch_mode: function(view_type, no_store, view_options) {
var self = this;
contexts: [action_context].concat(contexts || []),
group_by_seq: groupbys || []
}).done(function (results) {
- if (results.error) {
- throw new Error(
- _.str.sprintf(_t("Failed to evaluate search criterions")+": \n%s",
- JSON.stringify(results.error)));
- }
self.dataset._model = new instance.web.Model(
self.dataset.model, results.context, results.domain);
var groupby = results.group_by.length
this.dataset = dataset;
this.model_id = model_id;
if (args && args[0].error) {
- this.do_warn(_t('Uploading Error'), args[0].error);
+ this.do_warn( instance.web.qweb.render('message_error_uploading'), args[0].error);
}
if (!model_id) {
this.on_attachments_loaded([]);
"context": this.dataset.get_context(),
});
}
- return this.alive(view_loaded_def).then(function(r) {
+ return view_loaded_def.then(function(r) {
self.fields_view = r;
// add css classes that reflect the (absence of) access rights
self.$el.addClass('oe_view')
</t>
<t t-name="ViewManagerDebugViewLog">
<div class="oe_debug_view_log">
- <table class="table table-condensed table-striped">
- <tr>
- <th>ID:</th>
- <td><t t-esc="perm.id"/></td>
- </tr>
- <tr>
- <th>XML ID:</th>
- <td><t t-esc="perm.xmlid or '/'"/></td>
- </tr>
- <tr>
- <th>Creation User:</th>
- <td><t t-esc="format(perm.create_uid, { 'type' : 'many2one' }, '/')"/></td>
- </tr>
- <tr>
- <th>Creation Date:</th>
- <td><t t-esc="format(perm.create_date, { 'type' : 'datetime' }, '/')"/></td>
- </tr>
- <tr>
- <th>Latest Modification by:</th>
- <td><t t-esc="format(perm.write_uid, { 'type' : 'many2one' }, '/')"/></td>
- </tr>
- <tr>
- <th>Latest Modification Date:</th>
- <td><t t-esc="format(perm.write_date, { 'type' : 'datetime' }, '/')"/></td>
- </tr>
- </table>
+ <label>ID:</label>
+ <span><t t-esc="perm.id"/></span>
+
+ <label>XML ID:</label>
+ <span><t t-esc="perm.xmlid"/></span>
+
+ <label>Creation User:</label>
+ <span><t t-esc="format(perm.create_uid, { 'type' : 'many2one' }, '/')"/></span>
+
+ <label>Creation Date:</label>
+ <span><t t-esc="format(perm.create_date, { 'type' : 'datetime' }, '/')"/></span>
+
+ <label>Latest Modification by:</label>
+ <span><t t-esc="format(perm.write_uid, { 'type' : 'many2one' }, '/')"/></span>
+
+ <label>Latest Modification Date:</label>
+ <span><t t-esc="format(perm.write_date, { 'type' : 'datetime' }, '/')"/></span>
</div>
</t>
<t t-name="ViewPager">
_.str.toBoolElse = function(str, elseValues, trueValues, falseValues) {
var ret = _.str.toBool(str, trueValues, falseValues);
if (_.isUndefined(ret)) {
- return elseValues;
+ return elseValues
}
- return ret;
+ return ret
};
openerp.web_calendar = function(instance) {
}
function isNullOrUndef(value) {
- return _.isUndefined(value) || _.isNull(value);
+ return _.isUndefined(value) || _.isNull(value)
}
instance.web.views.add('calendar', 'instance.web_calendar.CalendarView');
this.range_start = null;
this.range_stop = null;
this.selected_filters = [];
+
},
set_default_options: function(options) {
destroy: function() {
this.$calendar.fullCalendar('destroy');
- if (this.$small_calendar){
- this.$small_calendar.datepicker('destroy');
- }
+ this.$small_calendar.datepicker('destroy');
this._super.apply(this, arguments);
},
this.date_start = attrs.date_start; // Field name of starting date field
this.date_delay = attrs.date_delay; // duration
this.date_stop = attrs.date_stop;
- this.all_day = attrs.all_day;
+ this.all_day = attrs.all_day;
+ this.attendee_people = attrs.attendee;
this.how_display_event = '';
- this.attendee_people = attrs.attendee;
-
+
if (!isNullOrUndef(attrs.quick_create_instance)) {
- self.quick_create_instance = 'instance.' + attrs.quick_create_instance;
+ self.quick_create_instance = 'instance.' + attrs.quick_create_instance;
}
//if quick_add = False, we don't allow quick_add
//if quick_add = not specified in view, we use the default quick_create_instance
//if quick_add = is NOT False and IS specified in view, we this one for quick_create_instance'
- this.quick_add_pop = (isNullOrUndef(attrs.quick_add) || _.str.toBoolElse(attrs.quick_add,true) );
+ this.quick_add_pop = (isNullOrUndef(attrs.quick_add) || _.str.toBoolElse(attrs.quick_add,true) );
if (this.quick_add_pop && !isNullOrUndef(attrs.quick_add)) {
- self.quick_create_instance = 'instance.' + attrs.quick_add;
+ self.quick_create_instance = 'instance.' + attrs.quick_add;
}
// The display format which will be used to display the event where fields are between "[" and "]"
if (!isNullOrUndef(attrs.display)) {
// If this field is set ot true, we don't open the event in form view, but in a popup with the view_id passed by this parameter
if (isNullOrUndef(attrs.event_open_popup) || !_.str.toBoolElse(attrs.event_open_popup,true)) {
- this.open_popup_action = false;
+ this.open_popup_action = false;
}
else {
this.open_popup_action = attrs.event_open_popup;
}
-
- // If this field is set to true, we will use the calendar_friends model as filter and not the color field.
- this.useContacts = (!isNullOrUndef(attrs.use_contacts) && _.str.toBool(attrs.use_contacts)) && (!isNullOrUndef(self.options.$sidebar));
+ // If this field is set to true, we will use de calendar_friends model as filter and not the color field.
+ this.useContacts = (!isNullOrUndef(attrs.use_contacts) && _.str.toBool(attrs.use_contacts));
// If this field is set ot true, we don't add itself as an attendee when we use attendee_people to add each attendee icon on an event
// The color is the color of the attendee, so don't need to show again that it will be present
- this.colorIsAttendee = (!(isNullOrUndef(attrs.color_is_attendee) || !_.str.toBoolElse(attrs.color_is_attendee, true))) && (!isNullOrUndef(self.options.$sidebar));
-
-
-
- // if we have not sidebar, (eg: Dashboard), we don't use the filter "coworkers"
- if (isNullOrUndef(self.options.$sidebar)) {
- this.useContacts = false;
- this.colorIsAttendee = false;
- this.attendee_people = undefined;
- }
-
+ this.colorIsAttendee = (!(isNullOrUndef(attrs.color_is_attendee) || !_.str.toBoolElse(attrs.color_is_attendee,true)));
/*
Will be more logic to do it in futur, but see below to stay Retro-compatible
}
*/
if (isNullOrUndef(attrs.avatar_model)) {
- this.avatar_model = null;
+ this.avatar_model = null;
}
else {
this.avatar_model = attrs.avatar_model;
}
if (isNullOrUndef(attrs.avatar_title)) {
- this.avatar_title = this.avatar_model;
+ this.avatar_title = this.avatar_model;
}
else {
this.avatar_title = attrs.avatar_title;
.call("check_access_rights", ["create", false])
.then(function (create_right) {
self.create_right = create_right;
- self.init_calendar().then(function() {
+ self.init_calendar().then(function() {
self.trigger('calendar_view_loaded', fv);
self.ready.resolve();
});
self.proxy('update_record')(event._id, data);
},
eventRender: function (event, element, view) {
- element.find('.fc-event-title').html(event.title);
+ element.find('.fc-event-title').html(event.title);
},
- eventAfterRender: function (event, element, view) {
+ eventAfterRender: function (event, element, view) {
if ((view.name !== 'month') && (((event.end-event.start)/60000)<=30)) {
//if duration is too small, we see the html code of img
var current_title = $(element.find('.fc-event-time')).text();
- var new_title = current_title.substr(0,current_title.indexOf("<img")>0?current_title.indexOf("<img"):current_title.length);
+ var new_title = current_title.substr(0,current_title.indexOf("<img")>0?current_title.indexOf("<img"):current_title.length)
element.find('.fc-event-time').html(new_title);
- }
+ }
},
eventClick: function (event) { self.open_event(event._id,event.title); },
select: function (start_date, end_date, all_day, _js_event, _view) {
end: end_date,
allDay: all_day,
});
+
self.open_quick_create(data_template);
},
agenda: 'h:mm{ - h:mm}', // 5:00 - 6:30
// for all other views
- '': 'h(:mm)tt' // 7pm
+ '': 'h(:mm)tt' // 7pm
},
weekMode : 'liquid',
aspectRatio: 1.8,
context.$calendar.fullCalendar('changeView','agendaDay');
}
}
- else if (curView.name != "agendaDay" || (curView.name == "agendaDay" && curDate.compareTo(curView.start)===0)) {
+ else if (curView.name != "agendaDay" || (curView.name == "agendaDay" && curDate.compareTo(curView.start)==0)) {
context.$calendar.fullCalendar('changeView','agendaWeek');
}
context.$calendar.fullCalendar('gotoDate', obj.currentYear , obj.currentMonth, obj.currentDay);
- };
+ }
},
init_calendar: function() {
this.$small_calendar.datepicker({ onSelect: self.calendarMiniChanged(self) });
if (this.useContacts) {
- //Get my Partner ID
new instance.web.Model("res.users").query(["partner_id"]).filter([["id", "=",this.dataset.context.uid]]).first()
.done(
- function(result) {
+ function(result) {
var sidebar_items = {};
var filter_value = result.partner_id[0];
var filter_item = {
sidebar_items[filter_value] = filter_item ;
filter_item = {
value: -1,
- label: _lt("Everybody's calendars"),
+ label: _lt("All calendars"),
color: self.get_color(-1),
avatar_model: self.avatar_model
};
sidebar_items[-1] = filter_item ;
- //Get my coworkers/contacts
+
new instance.web.Model("calendar.contacts").query(["partner_id"]).filter([["user_id", "=",self.dataset.context.uid]]).all().then(function(result) {
_.each(result, function(item) {
filter_value = item.partner_id[0];
self.allFilters = sidebar_items;
self.sidebar.filter.events_loaded(sidebar_items);
self.sidebar.filter.addUpdateButton();
- }).done(function () {
- self.$calendar.fullCalendar('refetchEvents');
+ }).done(function () {
+ self.$calendar.fullCalendar('refetchEvents');
});
}
- );
- }
+ );
+ };
this.extraSideBar();
}
delete this.quick;
this.$calendar.fullCalendar('unselect');
});
- this.quick.replace(this.$el.find('.oe_calendar_qc_placeholder'));
+ this.quick.replace($(".oe_calendar_qc_placeholder"));
this.quick.focus();
},
event_data_transform: function(evt) {
var self = this;
- var date_delay = evt[this.date_delay] || 1.0,
+ var date_start = instance.web.auto_str_to_date(evt[this.date_start]),
+ date_stop = this.date_stop ? instance.web.auto_str_to_date(evt[this.date_stop]) : null,
+ date_delay = evt[this.date_delay] || 1.0,
all_day = this.all_day ? evt[this.all_day] : false,
res_computed_text = '',
the_title = '',
- attendees = [];
-
- if (!all_day) {
- date_start = instance.web.auto_str_to_date(evt[this.date_start]);
- date_stop = this.date_stop ? instance.web.auto_str_to_date(evt[this.date_stop]) : null;
- }
- else {
- date_start = instance.web.auto_str_to_date(evt[this.date_start].split(' ')[0],'date');
- date_stop = this.date_stop ? instance.web.auto_str_to_date(evt[this.date_stop].split(' ')[0],'date').addMinutes(-1) : null;
- }
+ attendees = [];
if (this.info_fields) {
var temp_ret = {};
temp_ret[fieldname] = value;
}
res_computed_text = res_computed_text.replace("["+fieldname+"]",temp_ret[fieldname]);
- });
+ });
if (res_computed_text.length) {
}
else {
var res_text= [];
- _.each(temp_ret, function(val,key) { res_text.push(val); });
- the_title = res_text.join(', ');
+ _.each(temp_ret, function(val,key) { res_text.push(val)} );
+ the_title = res_text.join(', ');
}
the_title = _.escape(the_title);
var attendee_showed = 0;
var attendee_other = '';
- _.each(temp_ret[this.attendee_people],
- function (the_attendee_people) {
+ _.each(temp_ret[this.attendee_people],
+ function (the_attendee_people) {
attendees.push(the_attendee_people);
attendee_showed += 1;
- if (attendee_showed<= MAX_ATTENDEES) {
- if (self.avatar_model !== null) {
- the_title_avatar += '<img title="' + self.all_attendees[the_attendee_people] + '" class="attendee_head" \
- src="/web/binary/image?model=' + self.avatar_model + '&field=image_small&id=' + the_attendee_people + '"></img>';
+ if (attendee_showed<= MAX_ATTENDEES) {
+ if (self.avatar_model != null) {
+ the_title_avatar += '<img title="' + self.all_attendees[the_attendee_people] + '" class="attendee_head" src="/web/binary/image?model=' + self.avatar_model + '&field=image_small&id=' + the_attendee_people + '"></img>';
}
else {
if (!self.colorIsAttendee || the_attendee_people != temp_ret[self.color_field]) {
tempColor = (self.all_filters[the_attendee_people] != undefined)
? self.all_filters[the_attendee_people].color
: self.all_filters[-1].color;
- the_title_avatar += '<i class="fa fa-user attendee_head color_'+tempColor+'" title="' + self.all_attendees[the_attendee_people] + '" ></i>';
+
+ the_title_avatar += '<i class="fa fa-user attendee_head color_'+tempColor+'" title="' + self.all_attendees[the_attendee_people] + '" ></i>'
}//else don't add myself
}
}
else {
attendee_other += self.all_attendees[the_attendee_people] +", ";
- }
+ }
}
);
if (attendee_other.length>2) {
- the_title_avatar += '<span class="attendee_head" title="' + attendee_other.slice(0, -2) + '">+</span>';
+ the_title_avatar += '<span class="attendee_head" title="' + attendee_other.slice(0, -2) + '">+</span>';
}
the_title = the_title_avatar + the_title;
- }
+ }
}
if (!date_stop && date_delay) {
date_stop = date_start.clone().addHours(date_delay);
}
if (this.fields[this.date_start].type != "date" && all_day) {
- //date_stop.addDays(-1);
+ date_stop.addDays(-1);
}
var r = {
'start': date_start.toString('yyyy-MM-dd HH:mm:ss'),
};
- if (!self.useContacts || self.all_filters[evt[this.color_field]] !== undefined) {
+ if (!self.useContacts || self.all_filters[evt[this.color_field]] != undefined) {
if (this.color_field && evt[this.color_field]) {
var color_key = evt[this.color_field];
if (typeof color_key === "object") {
color_key = color_key[0];
}
- r.className = 'cal_opacity calendar_color_'+ this.get_color(color_key);
+ r.className = 'cal_opacity calendar_color_'+ this.get_color(color_key);
}
}
else { // if form all, get color -1
r.className = 'cal_opacity calendar_color_'+ self.all_filters[-1].color;
}
+
return r;
},
var event_end = event.end;
//Bug when we move an all_day event from week or day view, we don't have a dateend or duration...
- if (event_end === null) {
+ if (event_end == null) {
event_end = new Date(event.start).addHours(2);
}
event_end = new Date(event.start);
}
if (this.all_day) {
- event_end = (new Date(event_end.getTime())).addDays(1);
- date_start_day = new Date(event.start.getFullYear(),event.start.getMonth(),event.start.getDate(),12);
- date_stop_day = new Date(event_end.getFullYear(),event_end.getMonth(),event_end.getDate(),12);
+ event_end = (new Date(event_end.getTime())).addDays(1);
+ date_start_day = new Date(Date.UTC(event.start.getFullYear(),event.start.getMonth(),event.start.getDate()))
+ date_stop_day = new Date(Date.UTC(event_end.getFullYear(),event_end.getMonth(),event_end.getDate()))
}
else {
- date_start_day = new Date(event.start.getFullYear(),event.start.getMonth(),event.start.getDate(),7);
- date_stop_day = new Date(event_end.getFullYear(),event_end.getMonth(),event_end.getDate(),19);
- }
+ date_start_day = new Date(Date.UTC(event.start.getFullYear(),event.start.getMonth(),event.start.getDate(),7))
+ date_stop_day = new Date(Date.UTC(event_end.getFullYear(),event_end.getMonth(),event_end.getDate(),19))
+ }
data[this.date_start] = instance.web.parse_value(date_start_day, this.fields[this.date_start]);
if (this.date_stop) {
data[this.date_stop] = instance.web.parse_value(date_stop_day, this.fields[this.date_stop]);
}
else {
+
data[this.date_start] = instance.web.parse_value(event.start, this.fields[this.date_start]);
if (this.date_stop) {
data[this.date_stop] = instance.web.parse_value(event_end, this.fields[this.date_stop]);
- }
+ }
+
}
if (this.all_day) {
- data[this.all_day] = event.allDay;
+ data[this.all_day] = event.allDay;
}
if (this.date_delay) {
}
return data;
},
-
do_search: function(domain, context, _group_by) {
var self = this;
-
- if (self.sidebar) {
- self.sidebar.filter.is_loaded = false;
- }
-
if (! _.isUndefined(this.event_source)) {
this.$calendar.fullCalendar('removeEventSource', this.event_source);
}
domain: self.get_range_domain(domain, start, end),
context: context,
}).done(function(events) {
-
if (self.event_source !== current_event_source) {
console.log("Consecutive ``do_search`` called. Cancelling.");
return;
}
+ // We should make sure that *2many used in title of event have their extended form [ID, NAME]...
- if (self.attendee_people !== undefined) {
- //Else we filter on 'Everybody's Calendar, we don't filter events
- if (self.selected_filters.indexOf(-1) == -1) {
-
- //If we filter on contacts... we keep only events from coworkers
- events = $.map(events, function (e) {
- if (_.intersection(self.selected_filters,e[self.attendee_people]).length ) {
- return e;
- }
- return null;
- });
+ events = $.map(events, function (e) {
+
+ if (self.attendee_people != undefined) { //If we filter on contacts...
+ if (_.intersection(self.selected_filters,e[self.attendee_people]).length || (self.selected_filters.indexOf(-1) > -1)) {
+ return e;
+ }
+ else {
+ return null;
+ }
}
- }
+ else { //We adds all events
+ return e;
+ }
+ return null;
+ });
- if (!self.useContacts) { // If we use all peoples displayed in the current month as filter in sidebars
+ if (!self.useContacts) { // If we use all peoples as filter in sidebars
var now_filters = {};
var filter_value;
var filter_item;
-
_.each(events, function (e) {
filter_value = e[self.color_field][0];
filter_item = {
}
});
-
self.allFilters = now_filters;
-
- if (self.sidebar) {
- if (!self.sidebar.filter.is_loaded) {
- self.sidebar.filter.events_loaded(now_filters);
- }
- else {
- events = $.map(events, function (e) {
- if (_.contains(self.selected_filters,e[self.color_field][0]) ) {
- return e;
- }
- return null;
- });
- }
- }
+ self.sidebar.filter.events_loaded(now_filters);
+
return self.perform_necessary_name_gets(events).then(callback);
}
else {
var all_attendees = [];
_.each(events, function (e) {
- all_attendees.push(e[self.attendee_people]);
+ all_attendees.push(e[self.attendee_people]);
});
- self.all_attendees = {};
+ self.all_attendees = {}
all_attendees = _.chain(all_attendees).flatten().uniq().value();
- if (self.avatar_title !== null) {
+ if (self.avatar_title != null) {
new instance.web.Model(self.avatar_title).query(["name"]).filter([["id", "in",all_attendees]]).all().then(function(result) {
_.each(result, function(item) {
self.all_attendees[item.id] = item.name;
});
- }).done(function() {
+ }).done(function() {
return self.perform_necessary_name_gets(events).then(callback);
});
- }
+ }
else {
_.each(all_attendees,function(item){
self.all_attendees[item] = '';
return self.perform_necessary_name_gets(events).then(callback);
}
+
}
+
+
});
},
eventDataTransform: function (event) {
[this.date_start, '<=', format(end.clone())]]);
},
+
/**
* Updates record identified by ``id`` with values in object ``data``
*/
}
return false;
},
- open_event: function(id, title) {
+ open_event: function(id,title) {
var self = this;
- if (! this.open_popup_action) {
+ if (! this.open_popup_action) {
var index = this.dataset.get_id_index(id);
this.dataset.index = index;
this.do_switch_view('form', null, { mode: "edit" });
- }
+ }
else {
+
+ var self = this;
+
var pop = new instance.web.form.FormOpenPopup(this);
+ console.log(this.open_popup_action, +this.open_popup_action);
pop.show_element(this.dataset.model, id, this.dataset.get_context(), {
title: _.str.sprintf(_t("View: %s"),title),
view_id: +this.open_popup_action,
res_id: id,
target: 'new',
readonly:true
- });
+ });
var form_controller = pop.view_form;
form_controller.on("load_record", self, function(){
button_edit = _.str.sprintf("<button class='oe_button oe_bold editme oe_highlight'><span> %s </span></button>",_t("Edit Event"));
- pop.$el.closest(".ui-dialog").find(".ui-dialog-buttonpane").prepend(button_delete);
- pop.$el.closest(".ui-dialog").find(".ui-dialog-buttonpane").prepend(button_edit);
+ pop.$el.closest(".ui-dialog").find(".ui-dialog-buttonpane").prepend(button_delete)
+ pop.$el.closest(".ui-dialog").find(".ui-dialog-buttonpane").prepend(button_edit)
$('.delme').click(
- function() {
- $('.oe_form_button_cancel').trigger('click');
- self.remove_event(id);
+ function() {
+ $('.oe_form_button_cancel').trigger('click');
+ self.remove_event(id);
}
);
$('.editme').click(
- function() {
- $('.oe_form_button_cancel').trigger('click');
- self.dataset.index = self.dataset.get_id_index(id);
+ function() {
+ $('.oe_form_button_cancel').trigger('click');
+
+ var index = self.dataset.get_id_index(id);
+ self.dataset.index = index;
self.do_switch_view('form', null, { mode: "edit" });
+
}
);
+
+
});
- }
- return false;
+
+ }
+
+ return false;
+
},
do_show: function() {
slow_created: function () {
// refresh all view, because maybe some recurrents item
var self = this;
- if (self.sidebar) {
- // force filter refresh
- self.sidebar.filter.is_loaded = false;
- }
self.$calendar.fullCalendar('refetchEvents');
},
template: 'CalendarView.quick_create',
init: function(parent, dataset, buttons, options, data_template) {
- this._super(parent);
+ this._super(parent);
this.dataset = dataset;
this._buttons = buttons || false;
this.options = options;
get_title: function () {
var parent = this.getParent();
if (_.isUndefined(parent)) {
- return _t("Create");
+ return _t("Create")
}
var title = (_.isUndefined(parent.field_widget)) ?
(parent.string || parent.name) :
if(event.keyCode == 13){
self.$input.off('keyup', enterHandler);
if (!self.quick_add()){
- self.$input.on('keyup', enterHandler);
+ self.$input.on('keyup', enterHandler);
}
}
});
submit.click(function clickHandler() {
submit.off('click', clickHandler);
if (!self.quick_add()){
- submit.on('click', clickHandler); }
+ submit.on('click', clickHandler);
+ }
self.focus();
});
this.$el.find(".oe_calendar_quick_create_edit").click(function () {
self.slow_add();
self.focus();
- });
+ });
this.$el.find(".oe_calendar_quick_create_close").click(function (ev) {
ev.preventDefault();
self.trigger('close');
});
self.$el.on('dialogclose', self, function() {
+ console.log("dialogclose");
self.trigger('close');
});
*/
quick_add: function() {
var val = this.$input.val();
- if (/^\s*$/.test(val)) {
- return false;
+ if (/^\s*$/.test(val)) {
+ return false;
}
- return this.quick_create({'name': val}).always(function() { return true; });
+ return this.quick_create({'name': val}).always(function() { return true });
},
slow_add: function() {
return infos;
},
slow_create: function(data) {
- //if all day, we could reset time to display 00:00:00
-
var self = this;
var def = $.Deferred();
var defaults = {};
-
_.each($.extend({}, this.data_template, data), function(val, field_name) {
defaults['default_' + field_name] = val;
});
-
+
+ if (defaults['default_allday'] && (defaults['default_date_deadline'] || defaults['default_duration'])) {
+ delete defaults['default_date_deadline'];
+ delete defaults['default_duration'];
+ }
+
var pop_infos = self.get_form_popup_infos();
var pop = new instance.web.form.FormOpenPopup(this);
- var context = new instance.web.CompoundContext(this.dataset.context, defaults);
+ var context = new instance.web.CompoundContext(this.dataset.context, defaults)
pop.show_element(this.dataset.model, null, this.dataset.get_context(defaults), {
title: this.get_title(),
disable_multiple_selection: true,
},
render_value: function() {
+ console.log("In render value");
var self = this;
this.dataset.set_ids(this.get("value"));
this.is_loaded = this.is_loaded.then(function() {
},
alternative_form_view: this.field.views ? this.field.views.form : undefined,
- parent_view: this.view,
+ parent_view: this.view, //XXXvlab: to check ! this.view is likely undefined
child_name: this.name,
readonly: this.get("effective_readonly")
});
}
});
instance.web_calendar.SidebarFilter = instance.web.Widget.extend({
- is_loaded:false,
events: {
'change input:checkbox': 'filter_click'
},
init: function(parent, view) {
this._super(parent);
- this.view = view;
+ this.view = view;
},
-
-
-
events_loaded: function(filters) {
var self = this;
- self.is_loaded=true;
-
self.selected_filters = [];
self.view.all_filters = filters;
this.$el.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
- this.filter_click(null);
-
+ this.filter_click(null);
},
filter_click: function(e) {
var self = this,
this.$('div.oe_calendar_responsible input:checked').each(function() {
responsibles.push($(this).val());
- if (e==null && parseInt($(this).val())<0){
+ if (e==null && parseInt($(this).val())<0) {
$(this).prop('checked',false);
return;
- }
-
+ }
self.view.selected_filters.push(parseInt($(this).val()));
});
- self.view.$calendar.fullCalendar('refetchEvents');
+
+ if (e !== null) { //First intialize
+ self.view.$calendar.fullCalendar('refetchEvents');
+ }
+
+
},
addUpdateButton: function() {
var self=this;
this.$('div.oe_calendar_all_responsibles').append(QWeb.render('CalendarView.sidebar.button_add_contact'));
- this.$(".add_contacts_link_btn").on('click', function() {
- self.rpc("/web/action/load", {
- action_id: "calendar.action_calendar_contacts"
+ this.$(".add_contacts_link_btn").on('click', function() {
+ self.rpc("/web/action/load", {
+ action_id: "calendar.action_calendar_contacts"
}).then( function(result) { return self.do_action(result); });
});
var field_name = field.attrs.name;
if (_.has(field.attrs, 'interval')) {
field_name = field.attrs.name + ':' + field.attrs.interval;
- }
+ }
if (_.has(field.attrs, 'type')) {
switch (field.attrs.type) {
case 'row':
},
do_search: function (domain, context, group_by) {
- if (this.ignore_do_search) {
- this.ignore_do_search = false;
- return;
- }
-
var self = this,
- col_group_by = self.get_groupbys_from_searchview('ColGroupBy', 'col_group_by');
+ col_group_by = context.col_group_by || this.get_groupbys_from_searchview('ColGroupBy', 'col_group_by');
if (!this.graph_widget) {
if (group_by.length) {
this.widget_config.row_groupby = group_by;
}
if (col_group_by.length) {
- this.widget_config.col_groupby = col_group_by;
+ this.widget_config.col_groupby = group_by;
}
this.graph_widget = new openerp.web_graph.Graph(this, this.model, domain, this.widget_config);
this.graph_widget.appendTo(this.$el);
this.graph_widget.set(domain, group_by, col_group_by);
},
- extract_groupby: function (cat_field, context) {
- context = (_.isString(context)) ? py.eval(context) : context;
- return context[cat_field];
- },
-
get_groupbys_from_searchview: function (cat_name, cat_field) {
- var self=this,
- facet = this.search_view.query.findWhere({category:cat_name}),
+ var facet = this.search_view.query.findWhere({category:cat_name}),
groupby_list = facet ? facet.values.models : [];
return _.map(groupby_list, function (g) {
var context = g.attributes.value.attrs.context;
- return self.extract_groupby(cat_field, context);
+ if (_.isString(context)) {
+ return py.eval(context).group_by;
+ } else {
+ return context[cat_field];
+ }
});
},
if (!_.has(this.search_view, '_s_groupby')) { return; }
- if (row_groupby.length && col_groupby.length) {
- // when two changes to the search view will be done, the method do_search
- // will be called twice, once with the correct groupby and incorrect col_groupby,
- // and once with correct informations. This flag is necessary to prevent the
- // incorrect informations to propagate and trigger useless queries
- this.ignore_do_search = true;
- }
-
// add row groupbys
var row_facet = this.make_row_groupby_facets(row_groupby),
row_search_facet = query.findWhere({category:'GroupBy'});
case 'swap_axis':
this.swap_axis();
break;
+ case 'expand_all':
+ this.pivot.expand_all().then(this.proxy('display_data'));
+ break;
case 'update_values':
this.pivot.update_data().then(this.proxy('display_data'));
break;
if (header.expanded) {
this.fold(header);
- return;
- }
- if (header.path.length < header.root.groupby.length) {
- this.expand(id);
- return;
- }
- if (!this.important_fields.length) {
- return;
- }
+ } else {
+ if (header.path.length < header.root.groupby.length) {
+ this.expand(id);
+ } else {
+ if (!this.important_fields.length) {
+ return;
+ }
- var fields = _.map(this.important_fields, function (field) {
- return {id: field.field, value: field.string, type:self.fields[field.field.split(':')[0]].type};
- });
- if (this.dropdown) {
- this.dropdown.remove();
+ var fields = _.map(this.important_fields, function (field) {
+ return {id: field.field, value: field.string, type:self.fields[field.field.split(':')[0]].type};
+ });
+ this.dropdown = $(QWeb.render('field_selection', {fields:fields, header_id:id}));
+ $(event.target).after(this.dropdown);
+ this.dropdown.css({position:'absolute',
+ left:event.pageX,
+ top:event.pageY});
+ this.$('.field-selection').next('.dropdown-menu').toggle();
+ }
}
- this.dropdown = $(QWeb.render('field_selection', {fields:fields, header_id:id}));
- $(event.target).after(this.dropdown);
- this.dropdown.css({position:'absolute',
- left:event.pageX,
- top:event.pageY});
- this.$('.field-selection').next('.dropdown-menu').toggle();
-
-
},
field_selection: function (event) {
<label class="btn btn-default" data-choice="swap_axis" title="Swap Axis">
<span class="fa fa-expand"></span>
</label>
+ <label class="btn btn-default" data-choice="expand_all" title="Expand All">
+ <span class="fa fa-arrows-alt"></span>
+ </label>
<label class="btn btn-default" data-choice="update_values" title="Reload Data">
<span class="fa fa-refresh"></span>
</label>
}
evt.currentTarget.selectedIndex = 0;
}else{
- return this._super.apply(this,arguments);
+ return this._super.apply(this,arguments);
}
}
});
$.when(this.action_manager.do_action(action)).done(function() {
var viewmanager = self.action_manager.inner_widget;
var controller = viewmanager.views[viewmanager.active_view].controller;
- $(controller.groups).bind({
- 'selected': function (e, ids, records, deselected) {
+ controller.on('view_loaded', self, function(){
+ $(controller.groups).bind({
+ 'selected': function(e, ids, records) {
self.main_view_id = ids[0];
- }
+ }
+ });
});
});
},
return main_object;
},
parse_xml: function(arch, view_id) {
- //First element of att_list must be element tagname.
main_object = {
'level': 0,
'id': this.xml_element_id +=1,
- 'att_list': ["view"],
+ 'att_list': [],
'name': _.str.sprintf("<view view_id = %s>", view_id),
'child_id': []
};
var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null);
parent_tr = self.get_object_by_id(parseInt($(parent_tr).attr('id').replace(/[^0-9]+/g, '')), this.one_object['main_object'], [])[0].att_list[0];
_.each([tr, parent_tr],function(element) {
- var value = _.has(_CHILDREN, element) ? element : _.str.include(html_tag, element)?"html_tag":false;
+ var value = _.has(_CHILDREN, element) ? element : _.str.include(html_tag, element)?"html_tag":false;
property_to_check.push(value);
});
field_dataset.call( 'fields_get', []).done(function(result) {
var fields = _.keys(result);
fields.push(" "),fields.sort();
- self.on_add_node(property_to_check, fields, self.inject_position(parent_tr,tr));
+ self.on_add_node(property_to_check, fields);
});
},
- inject_position : function(parent_tag,current_tag){
- if(parent_tag == "view")
- return ['Inside'];
- if(current_tag == "field")
- return ['After','Before'];
- return ['After','Before','Inside'];
- },
do_node_edit: function(side) {
var self = this;
var result = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], []);
var children = _.filter(xml_arch.childNodes[0].childNodes, function (child) {
return child.nodeType == 1;
});
- var inherited_view = _.detect(children, function(xml_child) {
+ arch.arch = _.detect(children, function(xml_child) {
var temp_obj = self.create_View_Node(xml_child),
insert = _.intersection(_.flatten(temp_obj.att_list),_.uniq(check_list));
if (insert.length == _.uniq(check_list).length ) {return xml_child;}
});
- xml_arch = QWeb.load_xml(instance.web.xml_to_str(inherited_view));
+ xml_arch = QWeb.load_xml(arch.arch);
}
return self.do_save_xml(xml_arch.documentElement, obj[0].child_id[0],obj[0].child_id, move_direct, update_values,arch);
},
});
return def.promise();
},
- on_add_node: function(properties, fields, position){
+ on_add_node: function(properties, fields){
var self = this;
var render_list = [{'name': 'node_type','selection': _.keys(_CHILDREN).sort(), 'value': 'field', 'string': 'Node Type','type': 'selection'},
{'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'},
- {'name': 'position','selection': position, 'value': false, 'string': 'Position','type': 'selection'}];
+ {'name': 'position','selection': ['After','Before','Inside'], 'value': false, 'string': 'Position','type': 'selection'}];
this.add_widget = [];
this.add_node_dialog = new instance.web.Dialog(this,{
title: _t("Properties"),
//e.g.:xyz 'td' : ['field']
};
// Generic html_tag list and can be added html tag in future. It's support above _CHILDREN dict's *html_tag* by default.
-// For specific child node one has to define tag above and specify children tag in list. Like above xyz example.
+// For specific child node one has to define tag above and specify children tag in list. Like above xyz example.
var html_tag = ['div','h1','h2','h3','h4','h5','h6','td','tr'];
var _ICONS = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
import operator
import os
import time
-import datetime
-import dateutil
import openerp
from openerp import SUPERUSER_ID
'filter': fields.boolean('Filter'),
'auto_search':fields.boolean('Auto Search'),
'search_view' : fields.function(_search_view, type='text', string='Search View'),
- 'multi': fields.boolean('Restrict to lists', help="If checked and the action is bound to a model, it will only appear in the More menu on list views"),
+ 'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view"),
}
_defaults = {
if action.link_new_record and action.link_field_id:
self.pool[action.model_id.model].write(cr, uid, [context.get('active_id')], {action.link_field_id.name: res_id})
- def _eval_context_for_action(self, cr, uid, action, context=None):
- if context is None:
- context = {}
- model = self.pool[action.model_id.model]
- active_id = context.get('active_id')
- active_ids = context.get('active_ids', [active_id] if active_id else [])
- target_record = None
- if context.get('active_model') == action.model_id.model and active_id:
- context = dict(context, active_ids=active_ids, active_id=active_id)
- target_record = model.browse(cr, uid, active_id, context=context) if active_id else None
- user = self.pool['res.users'].browse(cr, uid, uid)
- eval_context = {
- 'self': model,
- 'object': target_record,
- 'obj': target_record,
- 'pool': self.pool,
- 'time': time,
- 'datetime': datetime,
- 'dateutil': dateutil,
- 'cr': cr,
- 'uid': uid,
- 'user': user,
- 'context': context,
- }
- return eval_context
-
-
def run(self, cr, uid, ids, context=None):
""" Runs the server action. For each server action, the condition is
checked. Note that a void (``False``) condition is considered as always
if context is None:
context = {}
res = False
+ user = self.pool.get('res.users').browse(cr, uid, uid)
+ active_ids = context.get('active_ids', [context.get('active_id')])
for action in self.browse(cr, uid, ids, context):
- eval_context = self._eval_context_for_action(cr, uid, action, context)
+ obj_pool = self.pool[action.model_id.model]
+ obj = None
+ if context.get('active_model') == action.model_id.model and context.get('active_id'):
+ obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
+
+ # evaluation context for python strings to evaluate
+ eval_context = {
+ 'self': obj_pool,
+ 'object': obj,
+ 'obj': obj,
+ 'pool': self.pool,
+ 'time': time,
+ 'cr': cr,
+ 'uid': uid,
+ 'user': user,
+ }
condition = action.condition
if condition is False:
# Void (aka False) conditions are considered as True
condition = True
if hasattr(self, 'run_action_%s_multi' % action.state):
- run_context = eval_context['context']
+ # set active_ids in context only needed if one active_id
+ run_context = dict(context, active_ids=active_ids)
+ eval_context["context"] = run_context
expr = eval(str(condition), eval_context)
if not expr:
continue
elif hasattr(self, 'run_action_%s' % action.state):
func = getattr(self, 'run_action_%s' % action.state)
- active_id = context.get('active_id')
- active_ids = context.get('active_ids', [active_id] if active_id else [])
for active_id in active_ids:
# run context dedicated to a particular active_id
run_context = dict(context, active_ids=[active_id], active_id=active_id)
<field name="auto_refresh"/>
<field name="auto_search"/>
<field name="filter"/>
- <field name="multi"/>
</group>
</group>
<group string="Help">
# Don't remove the LOG_ACCESS_COLUMNS unless _log_access
# has been turned off on the model.
field = self.pool[model].browse(cr, uid, [res_id], context=context)[0]
- if not field.exists():
- _logger.info('Deleting orphan external_ids %s', external_ids)
- self.unlink(cr, uid, external_ids)
- continue
if field.name in openerp.osv.orm.LOG_ACCESS_COLUMNS and self.pool[field.model]._log_access:
continue
if field.name == 'id':
return node
return None
- def inherit_branding(self, specs_tree, view_id, root_id):
+ def inherit_branding(self, specs_tree, view_id, source_id):
for node in specs_tree.iterchildren(tag=etree.Element):
xpath = node.getroottree().getpath(node)
if node.tag == 'data' or node.tag == 'xpath':
- self.inherit_branding(node, view_id, root_id)
+ self.inherit_branding(node, view_id, source_id)
else:
node.set('data-oe-id', str(view_id))
- node.set('data-oe-source-id', str(root_id))
+ node.set('data-oe-source-id', str(source_id))
node.set('data-oe-xpath', xpath)
node.set('data-oe-model', 'ir.ui.view')
node.set('data-oe-field', 'arch')
return source
- def apply_view_inheritance(self, cr, uid, source, source_id, model, root_id=None, context=None):
+ def apply_view_inheritance(self, cr, uid, source, source_id, model, context=None):
""" Apply all the (directly and indirectly) inheriting views.
:param source: a parent architecture to modify (with parent modifications already applied)
:return: a modified source where all the modifying architecture are applied
"""
if context is None: context = {}
- if root_id is None:
- root_id = source_id
sql_inherit = self.pool.get('ir.ui.view').get_inheriting_views_arch(cr, uid, source_id, model, context=context)
for (specs, view_id) in sql_inherit:
specs_tree = etree.fromstring(specs.encode('utf-8'))
if context.get('inherit_branding'):
- self.inherit_branding(specs_tree, view_id, root_id)
+ self.inherit_branding(specs_tree, view_id, source_id)
source = self.apply_inheritance_specs(cr, uid, source, specs_tree, view_id, context=context)
- source = self.apply_view_inheritance(cr, uid, source, view_id, model, root_id=root_id, context=context)
+ source = self.apply_view_inheritance(cr, uid, source, view_id, model, context=context)
return source
def read_combined(self, cr, uid, view_id, fields=None, context=None):
if f.tag in ('form', 'tree', 'graph', 'kanban', 'calendar'):
node.remove(f)
ctx = context.copy()
- ctx['base_model_name'] = model
+ ctx['base_model_name'] = Model
xarch, xfields = self.postprocess_and_fields(cr, user, column._obj or None, f, view_id, ctx)
views[str(f.tag)] = {
'arch': xarch,
# running index by tag type, for XPath query generation
indexes = collections.defaultdict(lambda: 0)
for child in e.iterchildren(tag=etree.Element):
- if child.get('data-oe-xpath'):
- # injected by view inheritance, skip otherwise
- # generated xpath is incorrect
- continue
indexes[child.tag] += 1
self.distribute_branding(child, distributed_branding,
parent_xpath=node_path,
for action in cr.dictfetchall():
if not action['value']:
continue # skip if undefined
- action_model_name, action_id = action['value'].split(',')
- action_model = self.pool.get(action_model_name)
- if not action_model:
- continue # unknow model? skip it
- fields = [field for field in action_model._all_columns if field not in EXCLUDED_FIELDS]
+ action_model,id = action['value'].split(',')
+ fields = [
+ field
+ for field in self.pool[action_model]._all_columns
+ if field not in EXCLUDED_FIELDS]
# FIXME: needs cleanup
try:
- action_def = action_model.read(cr, uid, int(action_id), fields, context)
+ action_def = self.pool[action_model].read(cr, uid, int(id), fields, context)
if action_def:
- if action_model_name in ('ir.actions.report.xml', 'ir.actions.act_window'):
+ if action_model in ('ir.actions.report.xml','ir.actions.act_window',
+ 'ir.actions.wizard'):
groups = action_def.get('groups_id')
if groups:
cr.execute('SELECT 1 FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',
'rml_footer': fields.text('Report Footer', help="Footer text displayed at the bottom of all reports."),
'rml_footer_readonly': fields.related('rml_footer', type='text', string='Report Footer', readonly=True),
'custom_footer': fields.boolean('Custom Footer', help="Check this to define the report footer manually. Otherwise it will be filled in automatically."),
- 'font': fields.many2one('res.font', string="Font", domain=[('mode', 'in', ('Normal', 'Regular', 'all', 'Book'))],
- help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
+ 'font': fields.many2one('res.font', string="Font",help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
'logo': fields.related('partner_id', 'image', string="Logo", type="binary"),
'logo_web': fields.function(_get_logo_web, string="Logo Web", type="binary", store={
'res.company': (lambda s, c, u, i, x: i, ['partner_id'], 10),
<label for="font" />
<div>
<div>
- <field name="font" class="oe_inline" colspan="2" on_change="onchange_font_name(font, rml_header, rml_header2, rml_header3)" />
+ <field name="font" class="oe_inline" colspan="2" on_change="onchange_font_name(font, rml_header, rml_header2, rml_header3)" domain="[('mode', 'in', ('normal', 'regular', 'all', 'book'))]" />
<button string="(reload fonts)" name="act_discover_fonts" type="object" class="oe_link" colspan="1"/>
</div>
</div>
context = dict(context or {})
context.pop('show_address', None)
context.pop('show_address_only', None)
- context.pop('show_email', None)
return dict(self.name_get(cr, uid, ids, context=context))
# indirections to avoid passing a copy of the overridable method when declaring the function field
for user in wizard.user_ids:
user_ids.append(user.id)
self.pool.get('change.password.user').change_password_button(cr, uid, user_ids, context=context)
- # don't keep temporary password copies in the database longer than necessary
- self.pool.get('change.password.user').unlink(cr, uid, user_ids)
return {
'type': 'ir.actions.act_window_close',
}
<field name="model">res.users</field>
<field name="arch" type="xml">
<search string="Users">
- <field name="name" filter_domain="['|', '|', ('name','ilike',self), ('login','ilike',self), ('email','ilike',self)]" string="User"/>
+ <field name="name" filter_domain="['|', '|', ('name','ilike',self), ('login','ilike',self), ('email,'ilike',self)]" string="User"/>
<field name="company_ids" string="Company" groups="base.group_multi_company"/>
</search>
</field>
{'name': 'Alice', 'login': 'alice', 'color': 1, 'function': 'Friend'},
{'name': 'Bob', 'login': 'bob', 'color': 2, 'function': 'Friend'},
{'name': 'Eve', 'login': 'eve', 'color': 3, 'function': 'Eavesdropper'},
- {'name': 'Nab', 'login': 'nab', 'color': 2, 'function': '5$ Wrench'},
]:
self.res_users.create(cr, uid, user_data)
self.assertIn('color', group_data, "Aggregated data for the column 'color' is not present in read_group return values")
self.assertEqual(group_data['color'], 3, "Incorrect sum for aggregated data for the column 'color'")
- groups_data = self.res_users.read_group(cr, uid, domain=[('login', 'in', ('alice', 'bob', 'eve'))], fields=['name', 'color'], groupby='name', orderby='name DESC, color asc')
- self.assertEqual(len(groups_data), 3, "Incorrect number of results when grouping on a field")
- self.assertEqual([user['name'] for user in groups_data], ['Eve', 'Bob', 'Alice'], 'Incorrect ordering of the list')
-
- groups_data = self.res_users.read_group(cr, uid, domain=[('login', 'in', ('alice', 'bob', 'eve', 'nab'))], fields=['function', 'color'], groupby='function', orderby='color ASC')
- self.assertEqual(len(groups_data), 3, "Incorrect number of results when grouping on a field")
- self.assertEqual(groups_data, sorted(groups_data, key=lambda x: x['color']), 'Incorrect ordering of the list')
-
class test_partner_recursion(common.TransactionCase):
def setUp(self):
ET.tostring(sarch, encoding='utf-8'),
ET.tostring(self.arch, encoding='utf-8'))
-class TestTemplating(common.TransactionCase):
- def setUp(self):
- import openerp.modules
- super(TestTemplating, self).setUp()
- self._pool = openerp.modules.registry.RegistryManager.get(common.DB)
- self._init = self._pool._init
- # fuck off
- self._pool._init = False
-
- def tearDown(self):
- self._pool._init = self._init
- super(TestTemplating, self).tearDown()
-
- def test_branding_inherit(self):
- Views = self.registry('ir.ui.view')
- id = Views.create(self.cr, self.uid, {
- 'name': "Base view",
- 'type': 'qweb',
- 'arch': """<root>
- <item order="1"/>
- </root>
- """
- })
- id2 = Views.create(self.cr, self.uid, {
- 'name': "Extension",
- 'type': 'qweb',
- 'inherit_id': id,
- 'arch': """<xpath expr="//item" position="before">
- <item order="2"/>
- </xpath>
- """
- })
-
- arch_string = Views.read_combined(
- self.cr, self.uid, id, fields=['arch'],
- context={'inherit_branding': True})['arch']
-
- arch = ET.fromstring(arch_string)
- Views.distribute_branding(arch)
-
- [initial] = arch.xpath('//item[@order=1]')
- self.assertEqual(
- str(id),
- initial.get('data-oe-id'),
- "initial should come from the root view")
- self.assertEqual(
- '/root[1]/item[1]',
- initial.get('data-oe-xpath'),
- "initial's xpath should be within the root view only")
-
- [second] = arch.xpath('//item[@order=2]')
- self.assertEqual(
- str(id2),
- second.get('data-oe-id'),
- "second should come from the extension view")
class test_views(common.TransactionCase):
def _get_by_id(self, obj, cr, uid, prop_name, ids, context=None):
prop = obj.pool.get('ir.property')
vids = [obj._name + ',' + str(oid) for oid in ids]
- domain = [('fields_id.model', '=', obj._name), ('fields_id.name', 'in', prop_name)]
- if context and context.get('company_id'):
- domain += [('company_id', '=', context.get('company_id'))]
+ def_id = self._field_get(cr, uid, obj._name, prop_name[0])
+ company = obj.pool.get('res.company')
+ cid = company._company_default_get(cr, uid, obj._name, def_id, context=context)
+ domain = [('fields_id.model', '=', obj._name), ('fields_id.name', 'in', prop_name), ('company_id', '=', cid)]
+ #domain = prop._get_domain(cr, uid, prop_name, obj._name, context)
if vids:
domain = [('res_id', 'in', vids)] + domain
return prop.search(cr, uid, domain, context=context)
if context is None:
context = {}
- def_id = self._field_get(cr, uid, obj._name, prop_name)
- company = obj.pool.get('res.company')
- cid = company._company_default_get(cr, uid, obj._name, def_id, context=context)
- # TODO for trunk: add new parameter company_id to _get_by_id method
- context_company = dict(context, company_id=cid)
- nids = self._get_by_id(obj, cr, uid, [prop_name], [id], context_company)
+ nids = self._get_by_id(obj, cr, uid, [prop_name], [id], context)
if nids:
cr.execute('DELETE FROM ir_property WHERE id IN %s', (tuple(nids),))
property_create = True
if property_create:
+ def_id = self._field_get(cr, uid, obj._name, prop_name)
+ company = obj.pool.get('res.company')
+ cid = company._company_default_get(cr, uid, obj._name, def_id,
+ context=context)
propdef = obj.pool.get('ir.model.fields').browse(cr, uid, def_id,
context=context)
prop = obj.pool.get('ir.property')
(name_id, context['module'], 'ir.model', model_id)
)
+ cr.commit()
+
cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,))
cols = {}
for rec in cr.dictfetchall():
for key, val in vals.items():
if cols[k][key] != vals[key]:
cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'], vals['model'], vals['name']))
+ cr.commit()
cr.execute("""UPDATE ir_model_fields SET
model_id=%s, field_description=%s, ttype=%s, relation=%s,
select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s, translate=%s, serialization_field_id=%s
vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], bool(vals['translate']), vals['serialization_field_id'], vals['model'], vals['name']
))
break
+ cr.commit()
#
# Goal: try to apply inheritance at the instanciation level and
r['__fold'] = folded.get(r[groupby] and r[groupby][0], False)
return result
- def _read_group_generate_order_by(self, orderby, aggregated_fields, groupby, query):
- """
- Generates the ORDER BY sql clause for the read group method. Adds the missing JOIN clause
- to the query if order should be computed against m2o field.
- :param orderby: the orderby definition in the form "%(field)s %(order)s"
- :param aggregated_fields: list of aggregated fields in the query
- :param groupby: the current groupby field name
- :param query: the query object used to construct the query afterwards
- """
- orderby_list = []
- ob = []
- for order_splits in orderby.split(','):
- order_split = order_splits.split()
- orderby_field = order_split[0]
- fields = openerp.osv.fields
- if isinstance(self._all_columns[orderby_field].column, (fields.date, fields.datetime)):
- continue
- orderby_dir = len(order_split) == 2 and order_split[1].upper() == 'ASC' and 'ASC' or 'DESC'
- if orderby_field == groupby:
- orderby_item = self._generate_order_by(order_splits, query).replace('ORDER BY ', '')
- if orderby_item:
- orderby_list.append(orderby_item)
- ob += [obi.split()[0] for obi in orderby_item.split(',')]
- elif orderby_field in aggregated_fields:
- orderby_list.append('%s %s' % (orderby_field,orderby_dir))
-
- if orderby_list:
- return ' ORDER BY %s' % (','.join(orderby_list)), ob and ','.join(ob) or ''
- else:
- return '', ''
-
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
"""
Get the list of records in list view grouped by the given ``groupby`` fields
qualified_field = self._inherits_join_calc(f, query)
flist += "%s(%s) AS %s" % (group_operator, qualified_field, f)
- order = orderby or groupby
- orderby_clause = ''
- ob = ''
- if order:
- orderby_clause, ob = self._read_group_generate_order_by(order, aggregated_fields, groupby, query)
-
gb = groupby and (' GROUP BY ' + qualified_groupby_field) or ''
from_clause, where_clause, where_clause_params = query.get_sql()
offset_str = offset and ' offset %d' % offset or ''
if len(groupby_list) < 2 and context.get('group_by_no_leaf'):
group_count = '_'
- cr.execute('SELECT min(%s.id) AS id, count(%s.id) AS %s_count' % (self._table, self._table, group_count) + (flist and ',') + flist + ' FROM ' + from_clause + where_clause + gb + (ob and ',') + ob + orderby_clause + limit_str + offset_str, where_clause_params)
+ cr.execute('SELECT min(%s.id) AS id, count(%s.id) AS %s_count' % (self._table, self._table, group_count) + (flist and ',') + flist + ' FROM ' + from_clause + where_clause + gb + limit_str + offset_str, where_clause_params)
alldata = {}
groupby = group_by
-
- fetched_data = cr.dictfetchall()
-
- data_ids = []
- for r in fetched_data:
+ for r in cr.dictfetchall():
for fld, val in r.items():
if val is None: r[fld] = False
alldata[r['id']] = r
- data_ids.append(r['id'])
del r['id']
+ order = orderby or groupby
+ data_ids = self.search(cr, uid, [('id', 'in', alldata.keys())], order=order, context=context)
+ # the IDs of records that have groupby field value = False or '' should be included too
+ data_ids += set(alldata.keys()).difference(data_ids)
if groupby:
data = self.read(cr, uid, data_ids, [groupby], context=context)
('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
]
if f_pg_type == 'varchar' and f._type == 'char' and ((f.size is None and f_pg_size) or f_pg_size < f.size):
- try:
- with cr.savepoint():
- cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" TYPE %s' % (self._table, k, pg_varchar(f.size)))
- except psycopg2.NotSupportedError:
- # In place alter table cannot be done because a view is depending of this field.
- # Do a manual copy. This will drop the view (that will be recreated later)
- cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
- cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size)))
- cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size)))
- cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
+ cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
+ cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size)))
+ cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size)))
+ cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
cr.commit()
_schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s",
self._table, k, f_pg_size or 'unlimited', f.size or 'unlimited')
if (f_pg_type==c[0]) and (f._type==c[1]):
if f_pg_type != f_obj_type:
ok = True
- cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO __temp_type_cast' % (self._table, k))
+ cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
- cr.execute(('UPDATE "%s" SET "%s"= __temp_type_cast'+c[3]) % (self._table, k))
- cr.execute('ALTER TABLE "%s" DROP COLUMN __temp_type_cast CASCADE' % (self._table,))
+ cr.execute(('UPDATE "%s" SET "%s"=temp_change_size'+c[3]) % (self._table, k))
+ cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
cr.commit()
_schema.debug("Table '%s': column '%s' changed type from %s to %s",
self._table, k, c[0], c[1])
blacklist_given_fields(self)
-
- fields_to_copy = dict((f,fi) for f, fi in self._all_columns.iteritems()
- if f not in default
- if f not in blacklist
- if not isinstance(fi.column, fields.function))
-
- data = self.read(cr, uid, [id], fields_to_copy.keys(), context=context)
+ fields_to_read = [f for f in self.check_field_access_rights(cr, uid, 'read', None)
+ if f not in blacklist]
+ data = self.read(cr, uid, [id], fields_to_read, context=context)
if data:
data = data[0]
else:
- raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name))
+ raise IndexError(_("Record #%d of %s not found, cannot copy!") % (id, self._name))
res = dict(default)
- for f, colinfo in fields_to_copy.iteritems():
+ for f, colinfo in self._all_columns.items():
field = colinfo.column
- if field._type == 'many2one':
+ if f in default:
+ pass
+ elif f in blacklist:
+ pass
+ elif isinstance(field, fields.function):
+ pass
+ elif field._type == 'many2one':
res[f] = data[f] and data[f][0]
elif field._type == 'one2many':
other = self.pool[field._obj]
cr.execute("select datname from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in %s order by datname", (db_user, templates_list))
else:
cr.execute("select datname from pg_database where datname not in %s order by datname", (templates_list,))
- res = [openerp.tools.ustr(name) for (name,) in cr.fetchall()]
+ res = [str(name) for (name,) in cr.fetchall()]
except Exception:
res = []
res.sort()
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
-# Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
+# Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
the ORM does, in fact.
"""
-from contextlib import contextmanager
from functools import wraps
import logging
-import time
-import uuid
import psycopg2.extensions
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_REPEATABLE_READ
from psycopg2.pool import PoolError
"""
return self._cnx.rollback()
- @contextmanager
- @check
- def savepoint(self):
- """context manager entering in a new savepoint"""
- name = uuid.uuid1().hex
- self.execute('SAVEPOINT "%s"' % name)
- try:
- yield
- self.execute('RELEASE SAVEPOINT "%s"' % name)
- except:
- self.execute('ROLLBACK TO SAVEPOINT "%s"' % name)
- raise
-
@check
def __getattr__(self, name):
return getattr(self._obj, name)
# restore res.partner fields
self.partner._columns = old_columns
-
-class TestPropertyField(common.TransactionCase):
-
- def setUp(self):
- super(TestPropertyField, self).setUp()
- self.user = self.registry('res.users')
- self.partner = self.registry('res.partner')
- self.company = self.registry('res.company')
- self.country = self.registry('res.country')
- self.property = self.registry('ir.property')
- self.imd = self.registry('ir.model.data')
-
- def test_1_property_multicompany(self):
- cr, uid = self.cr, self.uid
-
- parent_company_id = self.imd.get_object_reference(cr, uid, 'base', 'main_company')[1]
- country_be = self.imd.get_object_reference(cr, uid, 'base', 'be')[1]
- country_fr = self.imd.get_object_reference(cr, uid, 'base', 'fr')[1]
- group_partner_manager = self.imd.get_object_reference(cr, uid, 'base', 'group_partner_manager')[1]
- group_multi_company = self.imd.get_object_reference(cr, uid, 'base', 'group_multi_company')[1]
-
- sub_company = self.company.create(cr, uid, {'name': 'MegaCorp', 'parent_id': parent_company_id})
- alice = self.user.create(cr, uid, {'name': 'Alice',
- 'login':'alice',
- 'email':'alice@youcompany.com',
- 'company_id':parent_company_id,
- 'company_ids':[(6, 0, [parent_company_id, sub_company])],
- 'country_id':country_be,
- 'groups_id': [(6, 0, [group_partner_manager, group_multi_company])]
- })
- bob = self.user.create(cr, uid, {'name': 'Bob',
- 'login':'bob',
- 'email':'bob@megacorp.com',
- 'company_id':sub_company,
- 'company_ids':[(6, 0, [parent_company_id, sub_company])],
- 'country_id':country_fr,
- 'groups_id': [(6, 0, [group_partner_manager, group_multi_company])]
- })
-
- self.partner._columns = dict(self.partner._columns)
- self.partner._columns.update({
- 'property_country': fields.property(type='many2one', relation="res.country", string="Country by company"),
- })
- self.partner._all_columns.update({
- 'property_country': fields.column_info('property_country', self.partner._columns['property_country'], None, None, None),
- })
- self.partner._field_create(cr)
-
- partner_id = self.partner.create(cr, alice, {
- 'name': 'An International Partner',
- 'email': 'partner@example.com',
- 'company_id': parent_company_id,
- })
- self.partner.write(cr, bob, [partner_id], {'property_country': country_fr})
- self.assertEqual(self.partner.browse(cr, bob, partner_id).property_country.id, country_fr, "Bob does not see the value he has set on the property field")
-
- self.partner.write(cr, alice, [partner_id], {'property_country': country_be})
- self.assertEqual(self.partner.browse(cr, alice, partner_id).property_country.id, country_be, "Alice does not see the value he has set on the property field")
- self.assertEqual(self.partner.browse(cr, bob, partner_id).property_country.id, country_fr, "Changes made by Alice have overwritten Bob's value")
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
import StringIO
from PIL import Image
-from PIL import ImageEnhance
+from PIL import ImageOps
from random import randint
# ----------------------------------------
return base64_source
if image.size != size:
- # create a thumbnail: will resize and keep ratios, then sharpen for better looking result
- image.thumbnail(size, Image.ANTIALIAS)
- sharpener = ImageEnhance.Sharpness(image.convert('RGBA'))
- resized_image = sharpener.enhance(2.0)
- # create a transparent image for background and paste the image on it
- image = Image.new('RGBA', size, (255, 255, 255, 0))
- image.paste(resized_image, ((size[0] - resized_image.size[0]) / 2, (size[1] - resized_image.size[1]) / 2))
+ # If you need faster thumbnails you may use use Image.NEAREST
+ image = ImageOps.fit(image, size, Image.ANTIALIAS)
if image.mode not in ["1", "L", "P", "RGB", "RGBA"]:
image = image.convert("RGB")