[MERGE] forward port of branch saas-3 up to db75994
authorChristophe Simonis <chs@odoo.com>
Tue, 14 Oct 2014 13:13:14 +0000 (15:13 +0200)
committerChristophe Simonis <chs@odoo.com>
Tue, 14 Oct 2014 13:13:14 +0000 (15:13 +0200)
13 files changed:
1  2 
addons/account/account_bank_statement.py
addons/account/account_invoice.py
addons/email_template/email_template.py
addons/gamification/models/badge.py
addons/l10n_lu/__openerp__.py
addons/mail/mail_thread.py
addons/mail/tests/test_mail_gateway.py
addons/web/static/src/js/view_form.js
addons/website_membership/views/website_membership.xml
openerp/addons/base/ir/ir_mail_server.py
openerp/addons/base/ir/ir_ui_view.py
openerp/addons/base/res/res_users.py
openerp/tools/mail.py

@@@ -221,20 -193,57 +221,20 @@@ class account_bank_statement(osv.osv)
              'ref': st_line.ref,
          }
  
-     def _get_counter_part_account(sefl, cr, uid, st_line, context=None):
 -    def _prepare_bank_move_line(self, cr, uid, st_line, move_id, amount, company_currency_id,
 -        context=None):
 -        """Compute the args to build the dict of values to create the bank move line from a
 -           statement line by calling the _prepare_move_line_vals. This method may be
 -           overridden to implement custom move generation (making sure to call super() to
 -           establish a clean extension chain).
 -
 -           :param browse_record st_line: account.bank.statement.line record to
 -                  create the move from.
 -           :param int/long move_id: ID of the account.move to link the move line
 -           :param float amount: amount of the move line
 -           :param int/long company_currency_id: ID of currency of the concerned company
 -           :return: dict of value to create() the bank account.move.line
 -        """
 -        anl_id = st_line.analytic_account_id and st_line.analytic_account_id.id or False
 -        debit = ((amount<0) and -amount) or 0.0
 -        credit =  ((amount>0) and amount) or 0.0
 -        cur_id = False
 -        amt_cur = False
 -        if st_line.statement_id.currency.id <> company_currency_id:
 -            cur_id = st_line.statement_id.currency.id
 -        if st_line.account_id and st_line.account_id.currency_id and st_line.account_id.currency_id.id <> company_currency_id:
 -            cur_id = st_line.account_id.currency_id.id
 -        if cur_id:
 -            res_currency_obj = self.pool.get('res.currency')
 -            amt_cur = -res_currency_obj.compute(cr, uid, company_currency_id, cur_id, amount, context=context)
 -
 -        res = self._prepare_move_line_vals(cr, uid, st_line, move_id, debit, credit,
 -            amount_currency=amt_cur, currency_id=cur_id, analytic_id=anl_id, context=context)
 -        return res
 -
+     def _get_counter_part_account(self, cr, uid, st_line, context=None):
          """Retrieve the account to use in the counterpart move.
 -           This method may be overridden to implement custom move generation (making sure to
 -           call super() to establish a clean extension chain).
  
 -           :param browse_record st_line: account.bank.statement.line record to
 -                  create the move from.
 +           :param browse_record st_line: account.bank.statement.line record to create the move from.
             :return: int/long of the account.account to use as counterpart
          """
          if st_line.amount >= 0:
              return st_line.statement_id.journal_id.default_credit_account_id.id
          return st_line.statement_id.journal_id.default_debit_account_id.id
  
-     def _get_counter_part_partner(sefl, cr, uid, st_line, context=None):
+     def _get_counter_part_partner(self, cr, uid, st_line, context=None):
          """Retrieve the partner to use in the counterpart move.
 -           This method may be overridden to implement custom move generation (making sure to
 -           call super() to establish a clean extension chain).
  
 -           :param browse_record st_line: account.bank.statement.line record to
 -                  create the move from.
 +           :param browse_record st_line: account.bank.statement.line record to create the move from.
             :return: int/long of the res.partner to use as counterpart
          """
          return st_line.partner_id and st_line.partner_id.id or False
  #
  ##############################################################################
  
 -import time
 +import itertools
  from lxml import etree
 -import openerp.addons.decimal_precision as dp
 -import openerp.exceptions
  
 -from openerp.osv import fields, osv, orm
 +from openerp import models, fields, api, _
 +from openerp.exceptions import except_orm, Warning, RedirectWarning
+ from openerp.tools import float_compare
 -from openerp.tools.translate import _
 -from openerp import SUPERUSER_ID
 -
 -class account_invoice(osv.osv):
 -    def _amount_all(self, cr, uid, ids, name, args, context=None):
 -        res = {}
 -        for invoice in self.browse(cr, uid, ids, context=context):
 -            res[invoice.id] = {
 -                'amount_untaxed': 0.0,
 -                'amount_tax': 0.0,
 -                'amount_total': 0.0
 -            }
 -            for line in invoice.invoice_line:
 -                res[invoice.id]['amount_untaxed'] += line.price_subtotal
 -            for line in invoice.tax_line:
 -                res[invoice.id]['amount_tax'] += line.amount
 -            res[invoice.id]['amount_total'] = res[invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed']
 -        return res
 -
 -    def _get_journal(self, cr, uid, context=None):
 -        if context is None:
 -            context = {}
 -        type_inv = context.get('type', 'out_invoice')
 -        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
 -        company_id = context.get('company_id', user.company_id.id)
 -        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale_refund', 'in_refund': 'purchase_refund'}
 -        journal_obj = self.pool.get('account.journal')
 -        domain = [('company_id', '=', company_id)]
 -        if isinstance(type_inv, list):
 -            domain.append(('type', 'in', [type2journal.get(type) for type in type_inv if type2journal.get(type)]))
 -        else:
 -            domain.append(('type', '=', type2journal.get(type_inv, 'sale')))
 -        res = journal_obj.search(cr, uid, domain, limit=1)
 -        return res and res[0] or False
 -
 -    def _get_currency(self, cr, uid, context=None):
 -        res = False
 -        journal_id = self._get_journal(cr, uid, context=context)
 -        if journal_id:
 -            journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context)
 -            res = journal.currency and journal.currency.id or journal.company_id.currency_id.id
 -        return res
 -
 -    def _get_journal_analytic(self, cr, uid, type_inv, context=None):
 -        type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'}
 -        tt = type2journal.get(type_inv, 'sale')
 -        result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context)
 -        if not result:
 -            raise osv.except_osv(_('No Analytic Journal!'),_("You must define an analytic journal of type '%s'!") % (tt,))
 -        return result[0]
 -
 -    def _get_type(self, cr, uid, context=None):
 -        if context is None:
 -            context = {}
 -        return context.get('type', 'out_invoice')
 -
 -    def _reconciled(self, cr, uid, ids, name, args, context=None):
 -        res = {}
 -        for inv in self.browse(cr, uid, ids, context=context):
 -            res[inv.id] = self.test_paid(cr, uid, [inv.id])
 -            if not res[inv.id] and inv.state == 'paid':
 -                self.signal_open_test(cr, uid, [inv.id])
 -        return res
 -
 -    def _get_reference_type(self, cr, uid, context=None):
 -        return [('none', _('Free Reference'))]
 +import openerp.addons.decimal_precision as dp
  
 -    def _amount_residual(self, cr, uid, ids, name, args, context=None):
 -        """Function of the field residua. It computes the residual amount (balance) for each invoice"""
 -        if context is None:
 -            context = {}
 -        ctx = context.copy()
 -        result = {}
 -        currency_obj = self.pool.get('res.currency')
 -        for invoice in self.browse(cr, SUPERUSER_ID, ids, context=context):
 -            nb_inv_in_partial_rec = max_invoice_id = 0
 -            result[invoice.id] = 0.0
 -            if invoice.move_id:
 -                for aml in invoice.move_id.line_id:
 -                    if aml.account_id.type in ('receivable','payable'):
 -                        if aml.currency_id and aml.currency_id.id == invoice.currency_id.id:
 -                            result[invoice.id] += aml.amount_residual_currency
 -                        else:
 -                            ctx['date'] = aml.date
 -                            result[invoice.id] += currency_obj.compute(cr, uid, aml.company_id.currency_id.id, invoice.currency_id.id, aml.amount_residual, context=ctx)
 -
 -                        if aml.reconcile_partial_id.line_partial_ids:
 -                            #we check if the invoice is partially reconciled and if there are other invoices
 -                            #involved in this partial reconciliation (and we sum these invoices)
 -                            for line in aml.reconcile_partial_id.line_partial_ids:
 -                                if line.invoice and invoice.type == line.invoice.type:
 -                                    nb_inv_in_partial_rec += 1
 -                                    #store the max invoice id as for this invoice we will make a balance instead of a simple division
 -                                    max_invoice_id = max(max_invoice_id, line.invoice.id)
 -            if nb_inv_in_partial_rec:
 -                #if there are several invoices in a partial reconciliation, we split the residual by the number
 -                #of invoice to have a sum of residual amounts that matches the partner balance
 -                new_value = currency_obj.round(cr, uid, invoice.currency_id, result[invoice.id] / nb_inv_in_partial_rec)
 -                if invoice.id == max_invoice_id:
 -                    #if it's the last the invoice of the bunch of invoices partially reconciled together, we make a
 -                    #balance to avoid rounding errors
 -                    result[invoice.id] = result[invoice.id] - ((nb_inv_in_partial_rec - 1) * new_value)
 -                else:
 -                    result[invoice.id] = new_value
 +# mapping invoice type to journal type
 +TYPE2JOURNAL = {
 +    'out_invoice': 'sale',
 +    'in_invoice': 'purchase',
 +    'out_refund': 'sale_refund',
 +    'in_refund': 'purchase_refund',
 +}
  
 -            #prevent the residual amount on the invoice to be less than 0
 -            result[invoice.id] = max(result[invoice.id], 0.0)            
 -        return result
 +# mapping invoice type to refund type
 +TYPE2REFUND = {
 +    'out_invoice': 'out_refund',        # Customer Invoice
 +    'in_invoice': 'in_refund',          # Supplier Invoice
 +    'out_refund': 'out_invoice',        # Customer Refund
 +    'in_refund': 'in_invoice',          # Supplier Refund
 +}
  
 -    # Give Journal Items related to the payment reconciled to this invoice
 -    # Return ids of partial and total payments related to the selected invoices
 -    def _get_lines(self, cr, uid, ids, name, arg, context=None):
 -        res = {}
 -        for invoice in self.browse(cr, uid, ids, context=context):
 -            id = invoice.id
 -            res[id] = []
 -            if not invoice.move_id:
 -                continue
 -            data_lines = [x for x in invoice.move_id.line_id if x.account_id.id == invoice.account_id.id]
 -            partial_ids = []
 -            for line in data_lines:
 -                ids_line = []
 -                if line.reconcile_id:
 -                    ids_line = line.reconcile_id.line_id
 -                elif line.reconcile_partial_id:
 -                    ids_line = line.reconcile_partial_id.line_partial_ids
 -                l = map(lambda x: x.id, ids_line)
 -                partial_ids.append(line.id)
 -                res[id] =[x for x in l if x <> line.id and x not in partial_ids]
 -        return res
 +MAGIC_COLUMNS = ('id', 'create_uid', 'create_date', 'write_uid', 'write_date')
  
 -    def _get_invoice_line(self, cr, uid, ids, context=None):
 -        result = {}
 -        for line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context):
 -            result[line.invoice_id.id] = True
 -        return result.keys()
 -
 -    def _get_invoice_tax(self, cr, uid, ids, context=None):
 -        result = {}
 -        for tax in self.pool.get('account.invoice.tax').browse(cr, uid, ids, context=context):
 -            result[tax.invoice_id.id] = True
 -        return result.keys()
 -
 -    def _compute_lines(self, cr, uid, ids, name, args, context=None):
 -        result = {}
 -        for invoice in self.browse(cr, uid, ids, context=context):
 -            src = []
 -            lines = []
 -            if invoice.move_id:
 -                for m in invoice.move_id.line_id:
 -                    if m.account_id != invoice.account_id:
 -                        continue
 -                    temp_lines = []
 -                    if m.reconcile_id:
 -                        temp_lines = map(lambda x: x.id, m.reconcile_id.line_id)
 -                    elif m.reconcile_partial_id:
 -                        temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids)
 -                    lines += [x for x in temp_lines if x not in lines]
 -                    src.append(m.id)
 -
 -            lines = filter(lambda x: x not in src, lines)
 -            result[invoice.id] = lines
 -        return result
 -
 -    def _get_invoice_from_line(self, cr, uid, ids, context=None):
 -        move = {}
 -        for line in self.pool.get('account.move.line').browse(cr, uid, ids, context=context):
 -            if line.reconcile_partial_id:
 -                for line2 in line.reconcile_partial_id.line_partial_ids:
 -                    move[line2.move_id.id] = True
 -            if line.reconcile_id:
 -                for line2 in line.reconcile_id.line_id:
 -                    move[line2.move_id.id] = True
 -        invoice_ids = []
 -        if move:
 -            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
 -        return invoice_ids
 -
 -    def _get_invoice_from_reconcile(self, cr, uid, ids, context=None):
 -        move = {}
 -        for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids, context=context):
 -            for line in r.line_partial_ids:
 -                move[line.move_id.id] = True
 -            for line in r.line_id:
 -                move[line.move_id.id] = True
 -
 -        invoice_ids = []
 -        if move:
 -            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context)
 -        return invoice_ids
  
 +class account_invoice(models.Model):
      _name = "account.invoice"
      _inherit = ['mail.thread']
 -    _description = 'Invoice'
 -    _order = "id desc"
 +    _description = "Invoice"
 +    _order = "number desc, id desc"
      _track = {
          'type': {
          },
          """
          return move_lines
  
 -    def check_tax_lines(self, cr, uid, inv, compute_taxes, ait_obj):
 -        company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id
 -        if not inv.tax_line:
 +    @api.multi
 +    def check_tax_lines(self, compute_taxes):
 +        account_invoice_tax = self.env['account.invoice.tax']
 +        company_currency = self.company_id.currency_id
 +        if not self.tax_line:
              for tax in compute_taxes.values():
 -                ait_obj.create(cr, uid, tax)
 +                account_invoice_tax.create(tax)
          else:
              tax_key = []
 -            for tax in inv.tax_line:
++            precision = self.env['decimal.precision'].precision_get('Account')
 +            for tax in self.tax_line:
                  if tax.manual:
                      continue
                  key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id, tax.account_analytic_id.id)
                  tax_key.append(key)
 -                if not key in compute_taxes:
 -                    raise osv.except_osv(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !'))
 +                if key not in compute_taxes:
 +                    raise except_orm(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !'))
                  base = compute_taxes[key]['base']
-                 if abs(base - tax.base) > company_currency.rounding:
 -                precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account')
+                 if float_compare(abs(base - tax.base), company_currency.rounding, precision_digits=precision) == 1:
 -                    raise osv.except_osv(_('Warning!'), _('Tax base different!\nClick on compute to update the tax base.'))
 +                    raise except_orm(_('Warning!'), _('Tax base different!\nClick on compute to update the tax base.'))
              for key in compute_taxes:
 -                if not key in tax_key:
 -                    raise osv.except_osv(_('Warning!'), _('Taxes are missing!\nClick on compute button.'))
 +                if key not in tax_key:
 +                    raise except_orm(_('Warning!'), _('Taxes are missing!\nClick on compute button.'))
  
 -    def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines, context=None):
 -        if context is None:
 -            context={}
 +    @api.multi
 +    def compute_invoice_totals(self, company_currency, ref, invoice_move_lines):
          total = 0
          total_currency = 0
 -        cur_obj = self.pool.get('res.currency')
 -        for i in invoice_move_lines:
 -            if inv.currency_id.id != company_currency:
 -                context.update({'date': inv.date_invoice or fields.date.context_today(self, cr, uid, context=context)})
 -                i['currency_id'] = inv.currency_id.id
 -                i['amount_currency'] = i['price']
 -                i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id,
 -                        company_currency, i['price'],
 -                        context=context)
 +        for line in invoice_move_lines:
 +            if self.currency_id != company_currency:
 +                currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.context_today(self))
 +                line['currency_id'] = currency.id
 +                line['amount_currency'] = line['price']
 +                line['price'] = currency.compute(line['price'], company_currency)
              else:
 -                i['amount_currency'] = False
 -                i['currency_id'] = False
 -            i['ref'] = ref
 -            if inv.type in ('out_invoice','in_refund'):
 -                total += i['price']
 -                total_currency += i['amount_currency'] or i['price']
 -                i['price'] = - i['price']
 +                line['currency_id'] = False
 +                line['amount_currency'] = False
 +            line['ref'] = ref
 +            if self.type in ('out_invoice','in_refund'):
 +                total += line['price']
 +                total_currency += line['amount_currency'] or line['price']
 +                line['price'] = - line['price']
              else:
 -                total -= i['price']
 -                total_currency -= i['amount_currency'] or i['price']
 +                total -= line['price']
 +                total_currency -= line['amount_currency'] or line['price']
          return total, total_currency, invoice_move_lines
  
 -    def inv_line_characteristic_hashcode(self, invoice, invoice_line):
 +    def inv_line_characteristic_hashcode(self, invoice_line):
          """Overridable hashcode generation for invoice lines. Lines having the same hashcode
          will be grouped together if the journal has the 'group line' option. Of course a module
          can add fields to invoice lines that would need to be tested too before merging lines
@@@ -483,13 -449,12 +483,14 @@@ class email_template(osv.osv)
              # update values for all res_ids
              for res_id in template_res_ids:
                  values = results[res_id]
 +                # body: add user signature, sanitize
                  if 'body_html' in fields and template.user_signature:
                      signature = self.pool.get('res.users').browse(cr, uid, uid, context).signature
-                     values['body_html'] = tools.append_content_to_html(values['body_html'], signature, plaintext=False)
+                     if signature:
 -                        values['body_html'] = tools.append_content_to_html(values['body_html'], signature)
++                        values['body_html'] = tools.append_content_to_html(values['body_html'], signature, plaintext=False)
                  if values.get('body_html'):
                      values['body'] = tools.html_sanitize(values['body_html'])
 +                # technical settings
                  values.update(
                      mail_server_id=template.mail_server_id.id or False,
                      auto_delete=template.auto_delete,
@@@ -35,13 -35,11 +35,13 @@@ class gamification_badge_user(osv.Model
      _name = 'gamification.badge.user'
      _description = 'Gamification user badge'
      _order = "create_date desc"
 +    _rec_name = "badge_name"
  
      _columns = {
-         'user_id': fields.many2one('res.users', string="User", required=True),
+         'user_id': fields.many2one('res.users', string="User", required=True, ondelete="cascade"),
          'sender_id': fields.many2one('res.users', string="Sender", help="The user who has send the badge"),
 -        'badge_id': fields.many2one('gamification.badge', string='Badge', required=True),
 +        'badge_id': fields.many2one('gamification.badge', string='Badge', required=True, ondelete="cascade"),
 +        'challenge_id': fields.many2one('gamification.challenge', string='Challenge originating', help="If this badge was rewarded through a challenge"),
          'comment': fields.text('Comment'),
          'badge_name': fields.related('badge_id', 'name', type="char", string="Badge Name"),
          'create_date': fields.datetime('Created', readonly=True),
Simple merge
Simple merge
@@@ -164,7 -211,15 +211,15 @@@ class TestMailgateway(TestMail)
          self.assertIn('<div dir="ltr">Should create a multipart/mixed: from gmail, <b>bold</b>, with attachment.<br clear="all"><div><br></div>', res.get('body', ''),
                        'message_parse: html version should be in body after parsing multipart/mixed')
  
+         res = self.mail_thread.message_parse(cr, uid, MAIL_MULTIPART_MIXED_TWO)
+         self.assertNotIn('First and second part', res.get('body', ''),
+                          'message_parse: text version should not be in body after parsing multipart/mixed')
+         self.assertIn('First part', res.get('body', ''),
+                       'message_parse: first part of the html version should be in body after parsing multipart/mixed')
+         self.assertIn('Second part', res.get('body', ''),
+                       'message_parse: second part of the html version should be in body after parsing multipart/mixed')
 -    @mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
 +    @mute_logger('openerp.addons.mail.mail_thread', 'openerp.models')
      def test_10_message_process(self):
          """ Testing incoming emails processing. """
          cr, uid, user_raoul = self.cr, self.uid, self.user_raoul
@@@ -119,7 -117,9 +119,8 @@@ instance.web.FormView = instance.web.Vi
          });
          this.is_initialized = $.Deferred();
          this.mutating_mutex = new $.Mutex();
 -        this.on_change_list = [];
          this.save_list = [];
+         this.render_value_defs = [];
          this.reload_mutex = new $.Mutex();
          this.__clicked_inside = false;
          this.__blur_timeout = null;
  </template>
  
  <template id="opt_index_country" name="Location"
 -        inherit_option_id="website_membership.index" inherit_id="website_membership.index">
 -    <xpath expr="//div[@id='left_column']/ul[last()]" position="after">
 +        customize_show="True" inherit_id="website_membership.index">
 +    <xpath expr="//div[@id='left_column']/ul[1]" position="after">
          <ul class="nav nav-pills nav-stacked mt16">
              <li class="nav-header"><h3>Location</h3></li>
 -            <t t-foreach="countries">
 -                <li t-if="country_id" t-att-class="country_id and country_id[0] == current_country_id and 'active' or ''">
 -                    <a t-attf-href="/members/#{ membership and 'association/%s/' % membership.id or '' }#{ country_id[0] and 'country/%s/' % slug(country_id) or '' }#{ search }"><t t-esc="country_id[1]"/>
 -                        <span class="badge pull-right"><t t-esc="country_id_count or '0'"/></span>
 +            <t t-foreach="countries" t-as="country">
-                 <li t-if="country['country_id']" t-att-class="country['country_id'][0] == current_country_id and 'active' or ''">
++                <li t-if="country['country_id']" t-att-class="country['country_id'] and country['country_id'][0] == current_country_id and 'active' or ''">
 +                    <a t-attf-href="/members#{ membership and '/association/%s' % membership.id or '' }#{ country['country_id'][0] and '/country/%s' % slug(country['country_id']) or '' }#{ search }"><t t-esc="country['country_id'][1]"/>
 +                        <span class="badge pull-right"><t t-esc="country['country_id_count'] or '0'"/></span>
                      </a>
                  </li>
              </t>
@@@ -2,7 -2,7 +2,7 @@@
  ##############################################################################
  #
  #    OpenERP, Open Source Management Solution
--#    Copyright (C) 2011-2012 OpenERP S.A (<http://www.openerp.com>)
++#    Copyright (C) 2011-2014 OpenERP S.A. (<http://www.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
  #
  ##############################################################################
  
 -from email.MIMEText import MIMEText
 -from email.MIMEBase import MIMEBase
 -from email.MIMEMultipart import MIMEMultipart
 -from email.Charset import Charset
 -from email.Header import Header
 +from email.mime.text import MIMEText
 +from email.mime.base import MIMEBase
 +from email.mime.multipart import MIMEMultipart
 +from email.charset import Charset
 +from email.header import Header
- from email.utils import formatdate, make_msgid, COMMASPACE, parseaddr
+ from email.utils import formatdate, make_msgid, COMMASPACE, getaddresses, formataddr
  from email import Encoders
  import logging
  import re
@@@ -140,30 -140,15 +140,15 @@@ def encode_rfc2822_address_header(heade
         ``"Name"`` portion by the RFC2047-encoded
         version, preserving the address part untouched.
      """
-     header_text_utf8 = tools.ustr(header_text).encode('utf-8')
-     header_text_ascii = try_coerce_ascii(header_text_utf8)
-     if header_text_ascii:
-         return header_text_ascii
-     name, email = parseaddr(header_text_utf8)
-     if not name:
-       return email
-     # non-ASCII characters are present, attempt to
-     # replace all "Name" patterns with the RFC2047-
-     # encoded version
-     name_encoded = str(Header(name, 'utf-8'))
-     header_text_utf8 = "%s <%s>" % (name_encoded, email)
-     # try again after encoding
-     header_text_ascii = try_coerce_ascii(header_text_utf8)
-     if header_text_ascii:
-         return header_text_ascii
-     # fallback to extracting pure addresses only, which could
-     # still cause a failure downstream if the actual addresses
-     # contain non-ASCII characters
-     return COMMASPACE.join(extract_rfc2822_addresses(header_text_utf8))
-  
+     def encode_addr(addr):
+         name, email = addr
+         if not try_coerce_ascii(name):
+             name = str(Header(name, 'utf-8'))
+         return formataddr((name, email))
+     addresses = getaddresses([tools.ustr(header_text).encode('utf-8')])
+     return COMMASPACE.join(map(encode_addr, addresses))
 - 
++
  class ir_mail_server(osv.osv):
      """Represents an SMTP server, able to send outgoing emails, with SSL and TLS capabilities."""
      _name = "ir.mail_server"
@@@ -241,14 -177,8 +241,14 @@@ class view(osv.osv)
          if not cr.fetchone():
              cr.execute('CREATE INDEX ir_ui_view_model_type_inherit_id ON ir_ui_view (model, inherit_id)')
  
 +    def _compute_defaults(self, cr, uid, values, context=None):
 +        if 'inherit_id' in values:
 +            values.setdefault(
 +                'mode', 'extension' if values['inherit_id'] else 'primary')
 +        return values
 +
      def create(self, cr, uid, values, context=None):
-         if 'type' not in values:
+         if not values.get('type'):
              if values.get('inherit_id'):
                  values['type'] = self.browse(cr, uid, values['inherit_id'], context).type
              else:
Simple merge
Simple merge