[MERGE] Forward-port of latest 7.0 bugfixes, up to rev. 9846 revid:dle@openerp.com...
authorDenis Ledoux <dle@openerp.com>
Mon, 17 Feb 2014 12:42:30 +0000 (13:42 +0100)
committerDenis Ledoux <dle@openerp.com>
Mon, 17 Feb 2014 12:42:30 +0000 (13:42 +0100)
bzr revid: dle@openerp.com-20140214100922-m6rf7c6x85nv67sl
bzr revid: dle@openerp.com-20140214114713-oab4kbearvv7g3nh
bzr revid: dle@openerp.com-20140214131810-9abebxpfeoga1crn
bzr revid: dle@openerp.com-20140217124230-ov201kfep88f5tn7

addons/account/static/src/js/account_move_line_quickadd.js
addons/base_vat/base_vat.py
addons/crm/crm_lead.py
addons/event/event.py
addons/fetchmail/fetchmail.py
addons/mail/mail_thread.py
addons/mail/static/src/css/mail.css
addons/mail/tests/test_mail_gateway.py

index 997b2b0..0bbdb95 100644 (file)
@@ -22,6 +22,7 @@ openerp.account.quickadd = function (instance) {
         start:function(){
             var tmp = this._super.apply(this, arguments);
             var self = this;
+            var defs = [];
             this.$el.parent().prepend(QWeb.render("AccountMoveLineQuickAdd", {widget: this}));
             
             this.$el.parent().find('.oe_account_select_journal').change(function() {
@@ -41,11 +42,17 @@ openerp.account.quickadd = function (instance) {
                 self.$el.parent().find('.oe_account_select_period').removeAttr('disabled');
             });
             var mod = new instance.web.Model("account.move.line", self.dataset.context, self.dataset.domain);
-            mod.call("default_get", [['journal_id','period_id'],self.dataset.context]).then(function(result) {
+            defs.push(mod.call("default_get", [['journal_id','period_id'],self.dataset.context]).then(function(result) {
                 self.current_period = result['period_id'];
                 self.current_journal = result['journal_id'];
-            });
-            return tmp;
+            }));
+            defs.push(mod.call("list_journals", []).then(function(result) {
+                self.journals = result;
+            }));
+            defs.push(mod.call("list_periods", []).then(function(result) {
+                self.periods = result;
+            }));
+            return $.when(tmp, defs);
         },
         do_search: function(domain, context, group_by) {
             var self = this;
@@ -53,34 +60,27 @@ openerp.account.quickadd = function (instance) {
             this.last_context = context;
             this.last_group_by = group_by;
             this.old_search = _.bind(this._super, this);
-            var mod = new instance.web.Model("account.move.line", context, domain);
-            return $.when(mod.call("list_journals", []).then(function(result) {
-                self.journals = result;
-            }),mod.call("list_periods", []).then(function(result) {
-                self.periods = result;
-            })).then(function () {
-                var o;
-                self.$el.parent().find('.oe_account_select_journal').children().remove().end();
-                self.$el.parent().find('.oe_account_select_journal').append(new Option('', ''));
-                for (var i = 0;i < self.journals.length;i++){
-                    o = new Option(self.journals[i][1], self.journals[i][0]);
-                    if (self.journals[i][0] === self.current_journal){
-                        self.current_journal_type = self.journals[i][2];
-                        self.current_journal_currency = self.journals[i][3];
-                        self.current_journal_analytic = self.journals[i][4];
-                        $(o).attr('selected',true);
-                    }
-                    self.$el.parent().find('.oe_account_select_journal').append(o);
+            var o;
+            self.$el.parent().find('.oe_account_select_journal').children().remove().end();
+            self.$el.parent().find('.oe_account_select_journal').append(new Option('', ''));
+            for (var i = 0;i < self.journals.length;i++){
+                o = new Option(self.journals[i][1], self.journals[i][0]);
+                if (self.journals[i][0] === self.current_journal){
+                    self.current_journal_type = self.journals[i][2];
+                    self.current_journal_currency = self.journals[i][3];
+                    self.current_journal_analytic = self.journals[i][4];
+                    $(o).attr('selected',true);
                 }
-                self.$el.parent().find('.oe_account_select_period').children().remove().end();
-                self.$el.parent().find('.oe_account_select_period').append(new Option('', ''));
-                for (var i = 0;i < self.periods.length;i++){
-                    o = new Option(self.periods[i][1], self.periods[i][0]);
-                    self.$el.parent().find('.oe_account_select_period').append(o);
-                }    
-                self.$el.parent().find('.oe_account_select_period').val(self.current_period).attr('selected',true);
-                return self.search_by_journal_period();
-            });
+                self.$el.parent().find('.oe_account_select_journal').append(o);
+            }
+            self.$el.parent().find('.oe_account_select_period').children().remove().end();
+            self.$el.parent().find('.oe_account_select_period').append(new Option('', ''));
+            for (var i = 0;i < self.periods.length;i++){
+                o = new Option(self.periods[i][1], self.periods[i][0]);
+                self.$el.parent().find('.oe_account_select_period').append(o);
+            }    
+            self.$el.parent().find('.oe_account_select_period').val(self.current_period).attr('selected',true);
+            return self.search_by_journal_period();
         },
         search_by_journal_period: function() {
             var self = this;
@@ -93,7 +93,9 @@ openerp.account.quickadd = function (instance) {
             self.last_context["journal_type"] = self.current_journal_type;
             self.last_context["currency"] = self.current_journal_currency;
             self.last_context["analytic_journal_id"] = self.current_journal_analytic;
-            return self.old_search(new instance.web.CompoundDomain(self.last_domain, domain), self.last_context, self.last_group_by);
+            var compound_domain = new instance.web.CompoundDomain(self.last_domain, domain);
+            self.dataset.domain = compound_domain.eval();
+            return self.old_search(compound_domain, self.last_context, self.last_group_by);
         },
     });
 };
index 52db18b..e9ecf70 100644 (file)
@@ -54,7 +54,7 @@ _ref_vat = {
     'gr': 'GR12345670',
     'hu': 'HU12345676',
     'hr': 'HR01234567896', # Croatia, contributed by Milan Tribuson 
-    'ie': 'IE1234567T',
+    'ie': 'IE1234567FA',
     'it': 'IT12345670017',
     'lt': 'LT123456715',
     'lu': 'LU12345613',
@@ -190,6 +190,34 @@ class res_partner(osv.osv):
             return check == int(num[8])
         return False
 
+    def _ie_check_char(self, vat):
+        vat = vat.zfill(8)
+        extra = 0
+        if vat[7] not in ' W':
+            if vat[7].isalpha():
+                extra = 9 * (ord(vat[7]) - 64)
+            else:
+                # invalid
+                return -1
+        checksum = extra + sum((8-i) * int(x) for i, x in enumerate(vat[:7]))
+        return 'WABCDEFGHIJKLMNOPQRSTUV'[checksum % 23]
+
+    def check_vat_ie(self, vat):
+        """ Temporary Ireland VAT validation to support the new format
+        introduced in January 2013 in Ireland, until upstream is fixed.
+        TODO: remove when fixed upstream"""
+        if len(vat) not in (8, 9) or not vat[2:7].isdigit():
+            return False
+        if len(vat) == 8:
+            # Normalize pre-2013 numbers: final space or 'W' not significant
+            vat += ' '
+        if vat[:7].isdigit():
+            return vat[7] == self._ie_check_char(vat[:7] + vat[8])
+        elif vat[1] in (string.ascii_uppercase + '+*'):
+            # Deprecated format
+            # See http://www.revenue.ie/en/online/third-party-reporting/reporting-payment-details/faqs.html#section3
+            return vat[7] == self._ie_check_char(vat[2:7] + vat[0] + vat[8])
+        return False
 
     # Mexican VAT verification, contributed by <moylop260@hotmail.com>
     # and Panos Christeas <p_christ@hol.gr>
index 630c339..a7450cf 100644 (file)
@@ -329,6 +329,7 @@ class crm_lead(format_address, osv.osv):
                 'phone': partner.phone,
                 'mobile': partner.mobile,
                 'fax': partner.fax,
+                'zip': partner.zip,
             }
         return {'value': values}
 
index 3c04f5c..72894ea 100644 (file)
@@ -256,6 +256,7 @@ class event_event(osv.osv):
     ]
 
     def onchange_event_type(self, cr, uid, ids, type_event, context=None):
+        values = {}
         if type_event:
             type_info =  self.pool.get('event.type').browse(cr,uid,type_event,context)
             dic ={
@@ -265,7 +266,8 @@ class event_event(osv.osv):
               'register_min': type_info.default_registration_min,
               'register_max': type_info.default_registration_max,
             }
-            return {'value': dic}
+            values.update(dic)
+        return values
 
     def on_change_address_id(self, cr, uid, ids, address_id, context=None):
         values = {}
index cce1f77..5f63790 100644 (file)
@@ -187,7 +187,7 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
         for server in self.browse(cr, uid, ids, context=context):
             _logger.info('start checking for new emails on %s server %s', server.type, server.name)
             context.update({'fetchmail_server_id': server.id, 'server_type': server.type})
-            count = 0
+            count, failed = 0, 0
             imap_server = False
             pop_server = False
             if server.type == 'imap':
@@ -196,20 +196,26 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
                     imap_server.select()
                     result, data = imap_server.search(None, '(UNSEEN)')
                     for num in data[0].split():
+                        res_id = None
                         result, data = imap_server.fetch(num, '(RFC822)')
-                        res_id = mail_thread.message_process(cr, uid, server.object_id.model, 
-                                                             data[0][1],
-                                                             save_original=server.original,
-                                                             strip_attachments=(not server.attach),
-                                                             context=context)
+                        imap_server.store(num, '-FLAGS', '\\Seen')
+                        try:
+                            res_id = mail_thread.message_process(cr, uid, server.object_id.model,
+                                                                 data[0][1],
+                                                                 save_original=server.original,
+                                                                 strip_attachments=(not server.attach),
+                                                                 context=context)
+                        except Exception:
+                            _logger.exception('Failed to process mail from %s server %s.', server.type, server.name)
+                            failed += 1
                         if res_id and server.action_id:
-                            action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id], 'active_model': context.get("thread_model", server.object_id.model)})
-                            imap_server.store(num, '+FLAGS', '\\Seen')
-                            cr.commit()
+                            action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids': [res_id], 'active_model': context.get("thread_model", server.object_id.model)})
+                        imap_server.store(num, '+FLAGS', '\\Seen')
+                        cr.commit()
                         count += 1
-                    _logger.info("fetched/processed %s email(s) on %s server %s", count, server.type, server.name)
+                    _logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", count, server.type, server.name, (count - failed), failed)
                 except Exception:
-                    _logger.exception("Failed to fetch mail from %s server %s.", server.type, server.name)
+                    _logger.exception("General failure when trying to fetch mail from %s server %s.", server.type, server.name)
                 finally:
                     if imap_server:
                         imap_server.close()
@@ -222,18 +228,23 @@ openerp_mailgate: "|/path/to/openerp-mailgate.py --host=localhost -u %(uid)d -p
                     for num in range(1, numMsgs + 1):
                         (header, msges, octets) = pop_server.retr(num)
                         msg = '\n'.join(msges)
-                        res_id = mail_thread.message_process(cr, uid, server.object_id.model,
-                                                             msg,
-                                                             save_original=server.original,
-                                                             strip_attachments=(not server.attach),
-                                                             context=context)
+                        res_id = None
+                        try:
+                            res_id = mail_thread.message_process(cr, uid, server.object_id.model,
+                                                                 msg,
+                                                                 save_original=server.original,
+                                                                 strip_attachments=(not server.attach),
+                                                                 context=context)
+                        except Exception:
+                            _logger.exception('Failed to process mail from %s server %s.', server.type, server.name)
+                            failed += 1
                         if res_id and server.action_id:
-                            action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids':[res_id], 'active_model': context.get("thread_model", server.object_id.model)})
+                            action_pool.run(cr, uid, [server.action_id.id], {'active_id': res_id, 'active_ids': [res_id], 'active_model': context.get("thread_model", server.object_id.model)})
                         pop_server.dele(num)
                         cr.commit()
-                    _logger.info("fetched/processed %s email(s) on %s server %s", numMsgs, server.type, server.name)
+                    _logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", numMsgs, server.type, server.name, (numMsgs - failed), failed)
                 except Exception:
-                    _logger.exception("Failed to fetch mail from %s server %s.", server.type, server.name)
+                    _logger.exception("General failure when trying to fetch mail from %s server %s.", server.type, server.name)
                 finally:
                     if pop_server:
                         pop_server.quit()
index 71b6008..4c4a60b 100644 (file)
@@ -708,13 +708,15 @@ class mail_thread(osv.AbstractModel):
         # Private message: should not contain any thread_id
         if not model and thread_id:
             if assert_model:
-                assert thread_id == 0, 'Routing: posting a message without model should be with a null res_id (private message).'
+                if thread_id: 
+                    raise ValueError('Routing: posting a message without model should be with a null res_id (private message).')
             _warn('posting a message without model should be with a null res_id (private message), resetting thread_id')
             thread_id = 0
         # Private message: should have a parent_id (only answers)
         if not model and not message_dict.get('parent_id'):
             if assert_model:
-                assert message_dict.get('parent_id'), 'Routing: posting a message without model should be with a parent_id (private mesage).'
+                if not message_dict.get('parent_id'):
+                    raise ValueError('Routing: posting a message without model should be with a parent_id (private mesage).')
             _warn('posting a message without model should be with a parent_id (private mesage), skipping')
             return ()
 
@@ -743,7 +745,10 @@ class mail_thread(osv.AbstractModel):
         # New Document: check model accepts the mailgateway
         if not thread_id and model and not hasattr(model_pool, 'message_new'):
             if assert_model:
-                assert hasattr(model_pool, 'message_new'), 'Model %s does not accept document creation, crashing' % model
+                if not hasattr(model_pool, 'message_new'):
+                    raise ValueError(
+                        'Model %s does not accept document creation, crashing' % model
+                    )
             _warn('model %s does not accept document creation, skipping' % model)
             return ()
 
@@ -804,8 +809,11 @@ class mail_thread(osv.AbstractModel):
                to which this mail should be attached. Only used if the message
                does not reply to an existing thread and does not match any mail alias.
            :return: list of [model, thread_id, custom_values, user_id, alias]
+
+        :raises: ValueError, TypeError
         """
-        assert isinstance(message, Message), 'message must be an email.message.Message at this point'
+        if not isinstance(message, Message):
+            raise TypeError('message must be an email.message.Message at this point')
         fallback_model = model
 
         # Get email.message.Message variables for future processing
@@ -899,9 +907,11 @@ class mail_thread(osv.AbstractModel):
             return [route]
 
         # AssertionError if no routes found and if no bounce occured
-        assert False, \
-            "No possible route found for incoming message from %s to %s (Message-Id %s:)." \
-            "Create an appropriate mail.alias or force the destination model." % (email_from, email_to, message_id)
+        raise ValueError(
+                'No possible route found for incoming message from %s to %s (Message-Id %s:). '
+                'Create an appropriate mail.alias or force the destination model.' %
+                (email_from, email_to, message_id)
+            )
 
     def message_route_process(self, cr, uid, message, message_dict, routes, context=None):
         # postpone setting message_dict.partner_ids after message_post, to avoid double notifications
@@ -912,9 +922,11 @@ class mail_thread(osv.AbstractModel):
                 context.update({'thread_model': model})
             if model:
                 model_pool = self.pool[model]
-                assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
-                    "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
-                    (message_dict['message_id'], model)
+                if not (thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new')):
+                    raise ValueError(
+                        "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" %
+                        (message_dict['message_id'], model)
+                    )
 
                 # disabled subscriptions during message_new/update to avoid having the system user running the
                 # email gateway become a follower of all inbound messages
@@ -924,7 +936,8 @@ class mail_thread(osv.AbstractModel):
                 else:
                     thread_id = model_pool.message_new(cr, user_id, message_dict, custom_values, context=nosub_ctx)
             else:
-                assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
+                if thread_id:
+                    raise ValueError("Posting a message without model should be with a null res_id, to create a private message.")
                 model_pool = self.pool.get('mail.thread')
             if not hasattr(model_pool, 'message_post'):
                 context['thread_model'] = model
index c0d3bb9..be3bb38 100644 (file)
 .openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body p {
     margin-bottom: 0px;
 }
+.openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body pre {
+    white-space: pre-wrap;
+}
 .openerp .oe_mail .oe_msg .oe_msg_content .oe_msg_body * {
     text-overflow:ellipsis;
     word-wrap: break-word;
index b2a1c32..c756048 100644 (file)
@@ -465,14 +465,14 @@ class TestMailgateway(TestMail):
         # --------------------------------------------------
 
         # Do: incoming email with model that does not accepts incoming emails must raise
-        self.assertRaises(AssertionError,
+        self.assertRaises(ValueError,
                           format_and_process,
                           MAIL_TEMPLATE,
                           to='noone@example.com', subject='spam', extra='', model='res.country',
                           msg_id='<1198923581.41972151344608186760.JavaMail.new4@agrolait.com>')
 
         # Do: incoming email without model and without alias must raise
-        self.assertRaises(AssertionError,
+        self.assertRaises(ValueError,
                           format_and_process,
                           MAIL_TEMPLATE,
                           to='noone@example.com', subject='spam', extra='',