diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py
index 2f5f40df280..37bcefb2675 100644
--- a/addons/account/models/chart_template.py
+++ b/addons/account/models/chart_template.py
@@ -350,6 +350,26 @@ class AccountChartTemplate(models.Model):
new_xml_id = str(company.id)+'_'+template_xmlid.name
return ir_model_data._update(model, template_xmlid.module, vals, xml_id=new_xml_id, store=True, noupdate=True, mode='init', res_id=False)
+ def _get_account_vals(self, company, account_template, code_acc, tax_template_ref):
+ """ This method generates a dictionnary of all the values for the account that will be created.
+ """
+ self.ensure_one()
+ tax_ids = []
+ for tax in account_template.tax_ids:
+ tax_ids.append(tax_template_ref[tax.id])
+ val = {
+ 'name': account_template.name,
+ 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
+ 'code': code_acc,
+ 'user_type_id': account_template.user_type_id and account_template.user_type_id.id or False,
+ 'reconcile': account_template.reconcile,
+ 'note': account_template.note,
+ 'tax_ids': [(6, 0, tax_ids)],
+ 'company_id': company.id,
+ 'tag_ids': [(6, 0, [t.id for t in account_template.tag_ids])],
+ }
+ return val
+
@api.multi
def generate_account(self, tax_template_ref, acc_template_ref, code_digits, company):
""" This method for generating accounts from templates.
@@ -365,25 +385,11 @@ class AccountChartTemplate(models.Model):
account_tmpl_obj = self.env['account.account.template']
acc_template = account_tmpl_obj.search([('nocreate', '!=', True), ('chart_template_id', '=', self.id)], order='id')
for account_template in acc_template:
- tax_ids = []
- for tax in account_template.tax_ids:
- tax_ids.append(tax_template_ref[tax.id])
-
code_main = account_template.code and len(account_template.code) or 0
code_acc = account_template.code or ''
if code_main > 0 and code_main <= code_digits:
code_acc = str(code_acc) + (str('0'*(code_digits-code_main)))
- vals = {
- 'name': account_template.name,
- 'currency_id': account_template.currency_id and account_template.currency_id.id or False,
- 'code': code_acc,
- 'user_type_id': account_template.user_type_id and account_template.user_type_id.id or False,
- 'reconcile': account_template.reconcile,
- 'note': account_template.note,
- 'tax_ids': [(6, 0, tax_ids)],
- 'company_id': company.id,
- 'tag_ids': [(6, 0, [t.id for t in account_template.tag_ids])],
- }
+ vals = self._get_account_vals(company, account_template, code_acc, tax_template_ref)
new_account = self.create_record_with_xmlid(company, account_template, 'account.account', vals)
acc_template_ref[account_template.id] = new_account
return acc_template_ref
diff --git a/addons/account/views/web_planner_data.xml b/addons/account/views/web_planner_data.xml
index b3569deb76b..abd637d87d5 100644
--- a/addons/account/views/web_planner_data.xml
+++ b/addons/account/views/web_planner_data.xml
@@ -98,7 +98,7 @@
Terms & Conditions |
|
-
+
Change
|
diff --git a/addons/barcodes/static/src/js/form_view_barcode_handler.js b/addons/barcodes/static/src/js/form_view_barcode_handler.js
index 7f2614f0ef1..524ec1513c3 100644
--- a/addons/barcodes/static/src/js/form_view_barcode_handler.js
+++ b/addons/barcodes/static/src/js/form_view_barcode_handler.js
@@ -28,6 +28,8 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
this.__quantity_listener = _.bind(this._set_quantity_listener, this);
BarcodeHandlerMixin.init.apply(this, arguments);
+ this.process_barcode_mutex = new utils.Mutex();
+
return this._super.apply(this, arguments);
},
@@ -153,25 +155,35 @@ var FormViewBarcodeHandler = common.AbstractField.extend(BarcodeHandlerMixin, {
else if (this.form_view.get('actual_mode') === 'view')
this._display_no_edit_mode_warning();
else {
- // Call hook method possibly implemented by subclass
- this.pre_onchange_hook(barcode).then(function(proceed) {
- if (proceed === true) {
- // Wait for hypothetical ongoing onchange to finish
- self.form_view.onchanges_mutex.exec(function() {
- // A real onchange is triggered when a value actually changes (which can correspond
- // to a widget's blur event per example). Commit the value of fields before
- // programmatically triggering an onchange to be consistent with this.
- var mutex_commit_value = new utils.Mutex();
- _.each(self.form_view.fields, function(field) {
- mutex_commit_value.exec(_.bind(field.commit_value, field));
- });
- return mutex_commit_value.def.then(function(){
- // Trigger the barcode onchange
- self.set_value(barcode);
- });
- });
+ var process_barcode = function () {
+ // this function can be passed to `Mutex.exec` in order to make sure
+ // that every ongoing onchanges in the form view are done
+ var form_onchanges_mutex = function () {
+ return self.form_view.onchanges_mutex.def;
}
- });
+
+ // before setting the barcode field with the received barcode, we commit
+ // every fields of the form view and we wait for their hypothetical ongoing
+ // onchanges to finish
+ var commit_mutex = new utils.Mutex();
+ _.each(self.form_view.fields, function (field) {
+ commit_mutex.exec(function () {
+ return field.commit_value();
+ });
+ commit_mutex.exec(form_onchanges_mutex);
+ });
+
+ return commit_mutex.def.then(function () {
+ return self.pre_onchange_hook(barcode).then(function (proceed) {
+ if (proceed) {
+ self.set_value(barcode); // set the barcode field with the received one
+ return form_onchanges_mutex(); // wait for its onchange to finish
+ }
+ });
+ });
+ };
+
+ this.process_barcode_mutex.exec(process_barcode);
}
},
diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py
index 7a5fda8575e..007bbffeaf9 100644
--- a/addons/google_calendar/models/google_calendar.py
+++ b/addons/google_calendar/models/google_calendar.py
@@ -402,6 +402,20 @@ class GoogleCalendar(models.AbstractModel):
data_json = json.dumps(data)
return self.env['google.service']._do_request(url, data_json, headers, type='PUT')
+ def create_from_google(self, event, partner_id):
+ context_tmp = dict(self._context, NewMeeting=True)
+ res = self.with_context(context_tmp).update_from_google(False, event.GG.event, "create")
+ event.OE.event_id = res
+ meeting = self.env['calendar.event'].browse(res)
+ attendee_record = self.env['calendar.attendee'].search([('partner_id', '=', partner_id), ('event_id', '=', res)])
+ attendee_record.with_context(context_tmp).write({'oe_synchro_date': meeting.oe_update_date, 'google_internal_event_id': event.GG.event['id']})
+ if meeting.recurrency:
+ attendees = self.env['calendar.attendee'].sudo().search([('google_internal_event_id', '=ilike', '%s\_%%' % event.GG.event['id'])])
+ excluded_recurrent_event_ids = set(attendee.event_id for attendee in attendees)
+ for event in excluded_recurrent_event_ids:
+ event.write({'recurrent_id': meeting.id, 'recurrent_id_date': event.start, 'user_id': meeting.user_id.id})
+ return event
+
def update_from_google(self, event, single_event_dict, type):
""" Update an event in Odoo with information from google calendar
:param event : record od calendar.event to update
@@ -786,13 +800,8 @@ class GoogleCalendar(models.AbstractModel):
if isinstance(actToDo, NothingToDo):
continue
elif isinstance(actToDo, Create):
- context_tmp = {'newMeeting': True}
if actSrc == 'GG':
- res = recs.with_context(context_tmp).update_from_google(False, event.GG.event, "create")
- event.OE.event_id = res
- meeting = CalendarEvent.browse(res)
- attendee_records = CalendarAttendee.search([('partner_id', '=', my_partner_id), ('event_id', '=', res)])
- attendee_records.with_context(context_tmp).write({'oe_synchro_date': meeting.oe_update_date, 'google_internal_event_id': event.GG.event['id']})
+ self.create_from_google(event, my_partner_id)
elif actSrc == 'OE':
raise "Should be never here, creation for OE is done before update !"
#TODO Add to batch
@@ -817,6 +826,12 @@ class GoogleCalendar(models.AbstractModel):
main_ev = CalendarAttendee.with_context(context_novirtual).search([('google_internal_event_id', '=', event.GG.event['id'].rsplit('_', 1)[0])], limit=1)
event_to_synchronize[base_event][0][1].OE.event_id = main_ev.event_id.id
+ if event_to_synchronize[base_event][0][1].OE.event_id:
+ parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
+ res = self.update_from_google(parent_event, event.GG.event, "copy")
+ else:
+ self.create_from_google(event, my_partner_id)
+
parent_event['id'] = "%s-%s" % (event_to_synchronize[base_event][0][1].OE.event_id, new_google_event_id)
res = recs.update_from_google(parent_event, event.GG.event, "copy")
else:
diff --git a/addons/l10n_es/data/account_account_template_data.xml b/addons/l10n_es/data/account_account_template_data.xml
index 4c86cb55442..455d8132f04 100644
--- a/addons/l10n_es/data/account_account_template_data.xml
+++ b/addons/l10n_es/data/account_account_template_data.xml
@@ -1568,28 +1568,28 @@
4700
Hacienda Pública, deudora por IVA
-
+
4708
Hacienda Pública, deudora por subvenciones concedidas
-
+
4709
Hacienda Pública, deudora por devolución de impuestos
-
+
471
Organismos de la Seguridad Social, deudores
-
+
@@ -1603,77 +1603,77 @@
473
Hacienda Pública, retenciones y pagos a cuenta
-
+
4740
Activos por diferencias temporarias deducibles
-
+
4742
Derechos por deducciones y bonificaciones pendientes de aplicar
-
+
4745
Crédito por pérdidas a compensar del ejercicio
-
+
4750
Hacienda Pública, acreedora por IVA
-
+
4751
Hacienda Pública, acreedora por retenciones practicadas
-
+
4752
Hacienda Pública, acreedora por impuesto sobre sociedades
-
+
4758
Hacienda Pública, acreedora por subvenciones a reintegrar
-
+
476
Organismos de la Seguridad Social, acreedores
-
+
477
Hacienda Pública. IVA repercutido
-
+
479
Pasivos por diferencias temporarias imponibles
-
+
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index a6e20c46bd2..bf145c9d59e 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -271,8 +271,8 @@ class MrpProduction(models.Model):
boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id)
production._generate_raw_moves(lines)
# Check for all draft moves whether they are mto or not
- self._adjust_procure_method()
- self.move_raw_ids.action_confirm()
+ production._adjust_procure_method()
+ production.move_raw_ids.action_confirm()
return True
def _generate_finished_moves(self):
diff --git a/addons/payment_paypal/models/payment.py b/addons/payment_paypal/models/payment.py
index 6aaff94a631..60c5f57ea51 100644
--- a/addons/payment_paypal/models/payment.py
+++ b/addons/payment_paypal/models/payment.py
@@ -4,6 +4,9 @@ import json
import logging
import urlparse
+import dateutil.parser
+import pytz
+
from odoo import api, fields, models, _
from odoo.addons.payment.models.payment_acquirer import ValidationError
from odoo.addons.payment_paypal.controllers.main import PaypalController
@@ -193,7 +196,16 @@ class TxPaypal(models.Model):
}
if status in ['Completed', 'Processed']:
_logger.info('Validated Paypal payment for tx %s: set as done' % (self.reference))
- res.update(state='done', date_validate=data.get('payment_date', fields.datetime.now()))
+ try:
+ # dateutil and pytz don't recognize abbreviations PDT/PST
+ tzinfos = {
+ 'PST': -8 * 3600,
+ 'PDT': -7 * 3600,
+ }
+ date_validate = dateutil.parser.parse(data.get('payment_date'), tzinfos=tzinfos).astimezone(pytz.utc)
+ except:
+ date_validate = fields.Datetime.now()
+ res.update(state='done', date_validate=date_validate)
return self.write(res)
elif status in ['Pending', 'Expired']:
_logger.info('Received notification for Paypal payment %s: set as pending' % (self.reference))
diff --git a/addons/payment_paypal/tests/test_paypal.py b/addons/payment_paypal/tests/test_paypal.py
index 7cbe98f1a6b..c6a44dec41f 100644
--- a/addons/payment_paypal/tests/test_paypal.py
+++ b/addons/payment_paypal/tests/test_paypal.py
@@ -187,4 +187,4 @@ class PaypalForm(PaypalCommon):
# check
self.assertEqual(tx.state, 'done', 'paypal: wrong state after receiving a valid pending notification')
self.assertEqual(tx.acquirer_reference, '08D73520KX778924N', 'paypal: wrong txn_id after receiving a valid pending notification')
- self.assertEqual(tx.date_validate, '2013-11-18 03:21:19', 'paypal: wrong validation date')
+ self.assertEqual(tx.date_validate, '2013-11-18 11:21:19', 'paypal: wrong validation date')
diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js
index 5f61deb5e0c..f9cf49864d2 100644
--- a/addons/point_of_sale/static/src/js/screens.js
+++ b/addons/point_of_sale/static/src/js/screens.js
@@ -1432,6 +1432,7 @@ var ReceiptScreenWidget = ScreenWidget.extend({
print_xml: function() {
var env = {
widget: this,
+ order: this.pos.get_order(),
receipt: this.pos.get_order().export_for_printing(),
paymentlines: this.pos.get_order().get_paymentlines()
};
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index b88542f04e6..25fe10b5315 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -502,10 +502,14 @@ class SaleOrder(models.Model):
res = {}
currency = self.currency_id or self.company_id.currency_id
for line in self.order_line:
+ base_tax = 0
for tax in line.tax_id:
group = tax.tax_group_id
res.setdefault(group, 0.0)
- res[group] += tax.compute_all(line.price_reduce, quantity=line.product_uom_qty)['taxes'][0]['amount']
+ amount = tax.compute_all(line.price_reduce + base_tax, quantity=line.product_uom_qty)['taxes'][0]['amount']
+ res[group] += amount
+ if tax.include_base_amount:
+ base_tax += amount
res = sorted(res.items(), key=lambda l: l[0].sequence)
res = map(lambda l: (l[0].name, l[1]), res)
return res
diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py
index f52f6dc331d..9c95a6682ac 100644
--- a/addons/stock/models/stock_quant.py
+++ b/addons/stock/models/stock_quant.py
@@ -44,7 +44,7 @@ class Quant(models.Model):
lot_id = fields.Many2one(
'stock.production.lot', 'Lot/Serial Number',
index=True, ondelete="restrict", readonly=True)
- cost = fields.Float('Unit Cost')
+ cost = fields.Float('Unit Cost', group_operator='avg')
owner_id = fields.Many2one(
'res.partner', 'Owner',
index=True, readonly=True,
diff --git a/addons/web/static/src/js/views/list_view_editable.js b/addons/web/static/src/js/views/list_view_editable.js
index 3f5d39e60e1..ef9bf1a6062 100644
--- a/addons/web/static/src/js/views/list_view_editable.js
+++ b/addons/web/static/src/js/views/list_view_editable.js
@@ -641,7 +641,8 @@ ListView.include(/** @lends instance.web.ListView# */{
if (saveInfo.created) {
return self.start_edition();
}
- var record = self.records[next_record](saveInfo.record);
+ var options = { wraparound: !self.is_action_enabled('create') };
+ var record = self.records[next_record](saveInfo.record, options);
if (record === undefined) {
return self.start_edition();
}
diff --git a/addons/web/static/src/js/views/search_view.js b/addons/web/static/src/js/views/search_view.js
index 9f985fc3f49..65dbb4557e7 100644
--- a/addons/web/static/src/js/views/search_view.js
+++ b/addons/web/static/src/js/views/search_view.js
@@ -466,7 +466,7 @@ var SearchView = View.extend({
*/
select_completion: function (e, ui) {
e.preventDefault();
- if(ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
+ if(ui.item.facet && ui.item.facet.values && ui.item.facet.values.length && String(ui.item.facet.values[0].value).trim() !== "") {
this.query.add(ui.item.facet);
} else {
this.query.trigger('add');
diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js
index 9c6f1df5e8c..ce8643f04de 100644
--- a/addons/web_editor/static/src/js/backend.js
+++ b/addons/web_editor/static/src/js/backend.js
@@ -161,6 +161,11 @@ var FieldTextHtmlSimple = widget.extend({
return !this.get('value') || this.get('value') === "
" || !this.get('value').match(/\S/);
},
commit_value: function () {
+ /* Switch to WYSIWYG mode if currently in code view */
+ if (session.debug) {
+ var layoutInfo = this.$textarea.data('layoutInfo');
+ $.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
+ }
if (this.options['style-inline']) {
transcoder.class_to_style(this.$content);
transcoder.font_to_img(this.$content);
@@ -377,6 +382,11 @@ var FieldTextHtml = widget.extend({
this._dirty_flag = false;
return this.editor.save();
} else if (this._dirty_flag && this.editor && this.editor.buildingBlock) {
+ /* Switch to WYSIWYG mode if currently in code view */
+ if (session.debug) {
+ var layoutInfo = this.editor.rte.editable().data('layoutInfo');
+ $.summernote.pluginEvents.codeview(undefined, undefined, layoutInfo, false);
+ }
this.editor.buildingBlock.clean_for_save();
this.internal_set_value( this.$content.html() );
}
diff --git a/addons/web_editor/static/src/js/rte.summernote.js b/addons/web_editor/static/src/js/rte.summernote.js
index 1789ed69e7e..9f908a245bc 100644
--- a/addons/web_editor/static/src/js/rte.summernote.js
+++ b/addons/web_editor/static/src/js/rte.summernote.js
@@ -511,12 +511,32 @@ function prettify_html(html) {
}
return result;
}
-$.summernote.pluginEvents.codeview = function (event, editor, layoutInfo) {
+
+/*
+ * This override when clicking on the 'Code View' button has two aims:
+ *
+ * - have our own code view implementation for FieldTextHtml
+ * - add an 'enable' paramater to call the function directly and allow us to
+ * disable (false) or enable (true) the code view mode.
+ */
+$.summernote.pluginEvents.codeview = function (event, editor, layoutInfo, enable) {
+ if (layoutInfo === undefined) {
+ return;
+ }
if (layoutInfo.toolbar) {
+ // if editor inline (FieldTextHtmlSimple)
+ var is_activated = $.summernote.eventHandler.modules.codeview.isActivated(layoutInfo);
+ if (is_activated === enable) {
+ return;
+ }
return eventHandler.modules.codeview.toggle(layoutInfo);
} else {
+ // if editor iframe (FieldTextHtml)
var $editor = layoutInfo.editor();
var $textarea = $editor.prev('textarea');
+ if ($textarea.is('textarea') === enable) {
+ return;
+ }
if (!$textarea.length) {
// init and create texarea
diff --git a/addons/web_kanban/static/src/js/kanban_view.js b/addons/web_kanban/static/src/js/kanban_view.js
index 95d0510620b..7cce7617da9 100644
--- a/addons/web_kanban/static/src/js/kanban_view.js
+++ b/addons/web_kanban/static/src/js/kanban_view.js
@@ -710,24 +710,19 @@ var KanbanView = View.extend({
var context = {};
context['default_' + this.group_by_field] = column.id;
var name = event.data.value;
- this.dataset.name_create(name, context).then(function on_success (data) {
+ this.dataset.name_create(name).done(function(data) {
add_record(data[0]);
- }, function on_fail (event) {
+ }).fail(function(error, event) {
event.preventDefault();
- var popup = new form_common.SelectCreatePopup(this);
- popup.select_element(
- self.model,
- {
- title: _t("Create: "),
- initial_view: "form",
- disable_multiple_selection: true
- },
- [],
- {"default_name": name}
- );
- popup.on("elements_selected", self, function(element_ids) {
- add_record(element_ids[0]);
- });
+ var dialog = new form_common.FormViewDialog(self, {
+ res_model: self.model,
+ context: _.extend({"default_name": name}, context),
+ title: _t("Create"),
+ disable_multiple_selection: true,
+ on_selected: function(element_ids) {
+ add_record(element_ids[0]);
+ }
+ }).open();
});
function add_record(id) {
diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js
index a9e8ea86273..ae499fe2438 100644
--- a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js
+++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js
@@ -124,8 +124,11 @@ animation.registry.newsletter_popup = animation.Class.extend({
odoo.define('mass_mailing.unsubscribe', function (require) {
var ajax = require('web.ajax');
+ var core = require('web.core');
require('web_editor.base'); // wait for implicit dependencies to load
+ var _t = core._t;
+
if (!$('.o_unsubscribe_form').length) {
return $.Deferred().reject("DOM doesn't contain '.o_unsubscribe_form'");
}
@@ -148,10 +151,10 @@ odoo.define('mass_mailing.unsubscribe', function (require) {
ajax.jsonRpc('/mail/mailing/unsubscribe', 'call', {'opt_in_ids': checked_ids, 'opt_out_ids': unchecked_ids, 'email': email, 'mailing_id': mailing_id})
.then(function(result) {
- $('.alert-info').html('Your changes has been saved.').removeClass('alert-info').addClass('alert-success');
+ $('.alert-info').html(_t('Your changes have been saved.')).removeClass('alert-info').addClass('alert-success');
})
.fail(function() {
- $('.alert-info').html('You changes has not been saved, try again later.').removeClass('alert-info').addClass('alert-warning');
+ $('.alert-info').html(_t('Your changes have not been saved, try again later.')).removeClass('alert-info').addClass('alert-warning');
});
});
});
diff --git a/addons/website_portal_sale/controllers/main.py b/addons/website_portal_sale/controllers/main.py
index 40d840029a6..e421a9ce8b0 100644
--- a/addons/website_portal_sale/controllers/main.py
+++ b/addons/website_portal_sale/controllers/main.py
@@ -217,7 +217,7 @@ class website_account(website_account):
partner = request.env['res.users'].browse(request.uid).partner_id
invoices = request.env['account.invoice'].sudo().search_count([('partner_id', '=', partner.id), ('state', 'not in', ['draft', 'cancel'])])
if invoices:
- if data.get('vat', partner.vat) != partner.vat:
+ if (data.get('vat', partner.vat) or False) != partner.vat:
error['vat'] = 'error'
error_message.append(_('Changing VAT number is not allowed once invoices have been issued for your account. Please contact us directly for this operation.'))
if data.get('name', partner.name) != partner.name:
diff --git a/odoo-bin b/odoo-bin
index b2a0e17ff83..2670299b150 100755
--- a/odoo-bin
+++ b/odoo-bin
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+
+# set server timezone in UTC before time module imported
+__import__('os').environ['TZ'] = 'UTC'
__import__('pkg_resources').declare_namespace('odoo.addons')
import odoo
diff --git a/odoo/__init__.py b/odoo/__init__.py
index 69e10a4c011..dbf17c02200 100644
--- a/odoo/__init__.py
+++ b/odoo/__init__.py
@@ -27,15 +27,12 @@ multi_process = False
#----------------------------------------------------------
# libc UTC hack
#----------------------------------------------------------
-# Make sure the OpenERP server runs in UTC. This is especially necessary
-# under Windows as under Linux it seems the real import of time is
-# sufficiently deferred so that setting the TZ environment variable
-# in odoo.cli.server was working.
+# Make sure the OpenERP server runs in UTC.
import os
-os.environ['TZ'] = 'UTC' # Set the timezone...
-import time # ... *then* import time.
-del os
-del time
+os.environ['TZ'] = 'UTC' # Set the timezone
+import time
+if hasattr(time, 'tzset'):
+ time.tzset()
#----------------------------------------------------------
# Shortcuts
diff --git a/odoo/modules/loading.py b/odoo/modules/loading.py
index 7841b23fd0e..e9744844841 100644
--- a/odoo/modules/loading.py
+++ b/odoo/modules/loading.py
@@ -364,7 +364,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
for (model,) in cr.fetchall():
if model in registry:
env[model]._check_removed_columns(log=True)
- else:
+ elif _logger.isEnabledFor(logging.INFO): # more an info that a warning...
_logger.warning("Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model)
# Cleanup orphan records
@@ -402,8 +402,10 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
if update_module:
View = env['ir.ui.view']
for model in registry:
- if not View._validate_custom_views(model):
- _logger.warning('invalid custom view(s) for model %s', model)
+ try:
+ View._validate_custom_views(model)
+ except Exception as e:
+ _logger.warning('invalid custom view(s) for model %s: %s', model, tools.ustr(e))
if report.failures:
_logger.error('At least one test failed when loading the modules.')