[MERGE] forward port branch 11.0 up to 2787de8c68

This commit is contained in:
Christophe Simonis
2018-05-14 16:46:11 +02:00
24 changed files with 229 additions and 33 deletions
@@ -57,7 +57,7 @@
<span class="label label-default"><i class="fa fa-fw fa-remove"></i><span class="hidden-xs"> Cancelled</span></span>
</t>
</td>
<td><span t-field="invoice.residual" t-options='{"widget": "monetary", "display_currency": invoice.currency_id}'/></td>
<td><span t-esc="-invoice.residual if invoice.type == 'out_refund' else invoice.residual" t-options='{"widget": "monetary", "display_currency": invoice.currency_id}'/></td>
</tr>
</t>
</table></div>
@@ -5,7 +5,7 @@
</xpath>
<xpath expr="//t[@t-foreach='invoices']/tr/td[last()]" position="before">
<td>
<a t-if="invoice.state == 'open'" t-att-href="'/my/invoices/%s#portal_pay' % invoice.id" alt="Pay Now" class="btn btn-xs btn-primary">
<a t-if="invoice.state == 'open' and invoice.type == 'out_invoice'" t-att-href="'/my/invoices/%s#portal_pay' % invoice.id" alt="Pay Now" class="btn btn-xs btn-primary">
<i class="fa fa-arrow-circle-right"/><span class='hidden-xs'> Pay Now</span>
</a>
</td>
+6 -1
View File
@@ -1237,7 +1237,12 @@ class Meeting(models.Model):
def _rrule_parse(self, rule_str, data, date_start):
day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
rrule_type = ['yearly', 'monthly', 'weekly', 'daily']
rule = rrule.rrulestr(rule_str, dtstart=fields.Datetime.from_string(date_start))
ddate = fields.Datetime.from_string(date_start)
if 'Z' in rule_str and not ddate.tzinfo:
ddate = ddate.replace(tzinfo=pytz.timezone('UTC'))
rule = rrule.rrulestr(rule_str, dtstart=ddate)
else:
rule = rrule.rrulestr(rule_str, dtstart=ddate)
if rule._freq > 0 and rule._freq < 4:
data['rrule_type'] = rrule_type[rule._freq]
-1
View File
@@ -194,7 +194,6 @@
<group>
<field name="privacy"/>
<field name="show_as"/>
<field name="rrule" invisible="1" readonly="0" />
<field name="recurrent_id" invisible="1" />
</group>
</group>
+1 -1
View File
@@ -159,7 +159,7 @@ class StockPicking(models.Model):
def send_to_shipper(self):
self.ensure_one()
res = self.carrier_id.send_shipping(self)[0]
if self.carrier_id.free_over and self.sale_id._compute_amount_total_without_delivery() >= self.carrier_id.amount:
if self.carrier_id.free_over and self.sale_id and self.sale_id._compute_amount_total_without_delivery() >= self.carrier_id.amount:
res['exact_price'] = 0.0
self.carrier_price = res['exact_price']
self.carrier_tracking_ref = res['tracking_number']
+6 -2
View File
@@ -47,10 +47,14 @@ class GeoIPResolver(object):
r = self._db.city(ip)
except (ValueError, geoip2.errors.AddressNotFoundError):
return {}
# Compatibility with Legacy database.
# Some ips cannot be located to a specific country. Legacy DB used to locate them in
# continent instead of country. Do the same to not change behavior of existing code.
country = r.country if r.country.geoname_id else r.continent
return {
'city': r.city.name,
'country_code': r.country.iso_code,
'country_name': r.country.name,
'country_code': country.iso_code,
'country_name': country.name,
'region': r.subdivisions[0].iso_code if r.subdivisions else None,
'time_zone': r.location.time_zone,
}
+2
View File
@@ -1748,6 +1748,8 @@ class MailThread(models.AbstractModel):
continue
if isinstance(content, pycompat.text_type):
content = content.encode('utf-8')
elif content is None:
continue
data_attach = {
'name': name,
'datas': base64.b64encode(content),
+2 -1
View File
@@ -14,6 +14,7 @@ from werkzeug import urls
from odoo import api, fields, models, tools, _
from odoo.addons.payment.models.payment_acquirer import ValidationError
from odoo.addons.payment_adyen.controllers.main import AdyenController
from odoo.tools.pycompat import to_native
_logger = logging.getLogger(__name__)
@@ -192,7 +193,7 @@ class TxAdyen(models.Model):
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig_sha256('out', data)
else:
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig('out', data)
if shasign_check != data.get('merchantSig'):
if to_native(shasign_check) != to_native(data.get('merchantSig')):
error_msg = _('Adyen: invalid merchantSig, received %s, computed %s') % (data.get('merchantSig'), shasign_check)
_logger.warning(error_msg)
raise ValidationError(error_msg)
+1 -1
View File
@@ -175,7 +175,7 @@ class PosOrder(models.Model):
'comment': self.note or '',
# considering partner's sale pricelist's currency
'currency_id': self.pricelist_id.currency_id.id,
'user_id': self.env.uid,
'user_id': self.user_id.id,
}
@api.model
@@ -1172,9 +1172,11 @@ var ClientListScreenWidget = ScreenWidget.extend({
var order = this.pos.get_order();
if( this.has_client_changed() ){
var default_fiscal_position_id = _.findWhere(this.pos.fiscal_positions, {'id': this.pos.config.default_fiscal_position_id[0]});
if ( this.new_client && this.new_client.property_account_position_id ) {
var client_fiscal_position_id = _.findWhere(this.pos.fiscal_positions, {'id': this.new_client.property_account_position_id[0]});
order.fiscal_position = client_fiscal_position_id || default_fiscal_position_id;
if ( this.new_client ) {
if (this.new_client.property_account_position_id ){
var client_fiscal_position_id = _.findWhere(this.pos.fiscal_positions, {'id': this.new_client.property_account_position_id[0]});
order.fiscal_position = client_fiscal_position_id || default_fiscal_position_id;
}
order.set_pricelist(_.findWhere(this.pos.pricelists, {'id': this.new_client.property_product_pricelist[0]}) || this.pos.default_pricelist);
} else {
order.fiscal_position = default_fiscal_position_id;
@@ -22,7 +22,8 @@
</tr></thead>
<tbody>
<tr t-foreach='products' t-as='line'>
<td><t t-esc="line['product_name']" /></td>
<t t-set="internal_reference" t-value="line['code'] and '[%s] ' % line['code'] or ''" />
<td><t t-esc="internal_reference" /><t t-esc="line['product_name']" /></td>
<td>
<t t-esc="line['quantity']" />
<t t-if='line["uom"] != "Unit(s)"'>
+2 -2
View File
@@ -1060,10 +1060,10 @@ class ProcurementRule(models.Model):
'partner_id': partner.id,
'picking_type_id': self.picking_type_id.id,
'company_id': values['company_id'].id,
'currency_id': partner.property_purchase_currency_id.id or self.env.user.company_id.currency_id.id,
'currency_id': partner.with_context(force_company=values['company_id'].id).property_purchase_currency_id.id or self.env.user.company_id.currency_id.id,
'dest_address_id': values.get('partner_dest_id', False) and values['partner_dest_id'].id,
'origin': origin,
'payment_term_id': partner.property_supplier_payment_term_id.id,
'payment_term_id': partner.with_context(force_company=values['company_id'].id).property_supplier_payment_term_id.id,
'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'fiscal_position_id': fpos,
'group_id': group
@@ -57,6 +57,7 @@ class PosSaleReport(models.Model):
(cr.date_end IS NULL OR cr.date_end > COALESCE(so.date_order, now())))
LEFT JOIN product_uom u on (u.id=sol.product_uom)
LEFT JOIN product_uom u2 on (u2.id=pt.uom_id)
WHERE so.state != 'cancel'
""" % self.env['res.currency']._select_companies_rates()
return so_str
@@ -186,4 +186,5 @@ class SaleAdvancePaymentInv(models.TransientModel):
'invoice_policy': 'order',
'property_account_income_id': self.deposit_account_id.id,
'taxes_id': [(6, 0, self.deposit_taxes_id.ids)],
'company_id': False,
}
@@ -19,6 +19,11 @@ class TestSaleTimesheet(TestSale):
# after the 6 june of current year.
self.env.ref('base.rateUSDbis').unlink()
# Make sure the company is in USD
self.env.cr.execute(
"UPDATE res_company SET currency_id = %s WHERE id = %s",
[self.env.ref('base.USD').id, self.env.user.company_id.id])
# create project
self.project = self.env['project.project'].create({
'name': 'Project for my timesheets',
+142 -1
View File
@@ -290,4 +290,145 @@ X-Attachment-Id: ii_151b51a290ed6a91
R0lGODdhAgACALMAAAAAAP///wAAAP//AP8AAP+AAAD/AAAAAAAA//8A/wAAAP+AgAAAAAAAAAAA
AAAAACwAAAAAAgACAAAEA3DJFQA7
--001a11416b9e9b229a05272b7052--
"""
"""
MAIL_EML_ATTACHMENT = """Subject: Re: test attac
From: Anon <anon@odoo.com>
To: anon@gmail.com
References: <f3b9f8f8-28fa-2543-cab2-7aa68f679ebb@odoo.com>
Message-ID: <cb7eaf62-58dc-2017-148c-305d0c78892f@odoo.com>
Date: Wed, 14 Mar 2018 14:26:58 +0100
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101
Thunderbird/52.6.0
MIME-Version: 1.0
In-Reply-To: <f3b9f8f8-28fa-2543-cab2-7aa68f679ebb@odoo.com>
Content-Type: multipart/mixed;
boundary="------------A6B5FD5F68F4D73ECD739009"
Content-Language: en-US
This is a multi-part message in MIME format.
--------------A6B5FD5F68F4D73ECD739009
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
On 14/03/18 14:20, Anon wrote:
> Some nice content
>
--------------A6B5FD5F68F4D73ECD739009
Content-Type: message/rfc822;
name="original_msg.eml"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment;
filename="original_msg.eml"
Delivered-To: anon2@gmail1.openerp.com
Received: by 10.46.1.170 with SMTP id f42csp2379722lji;
Mon, 5 Mar 2018 01:19:23 -0800 (PST)
X-Google-Smtp-Source: AG47ELsYTlAcblMxfnaEENQuF+MFoac5Q07wieyw0cybq/qOX4+DmayqoQILkiWT+NiTOcnr/ACO
X-Received: by 10.28.154.213 with SMTP id c204mr7237750wme.64.1520241563503;
Mon, 05 Mar 2018 01:19:23 -0800 (PST)
ARC-Seal: i=1; a=rsa-sha256; t=1520241563; cv=none;
d=google.com; s=arc-20160816;
b=BqgMSbqmbpYW1ZtfGTVjj/654MBmabw4XadNZEaI96hDaub6N6cP8Guu3PoxscI9os
0OLYVP1s/B+Vv9rIzulCwHyHsgnX+aTxGYepTDN6x8SA9Qeb9aQoNSVvQLryTAoGpaFr
vXhw8aPWyr28edE03TDFA/s7X65Bf6dV5zJdMiUPVqGkfYfcTHMf3nDER5vk8vQj7tve
Cfyy0h9vLU9RSEtdFwmlEkLmgT9NQ3GDf0jQ97eMXPgR2q6duCPoMcz15KlWOno53xgH
EiV7aIZ5ZMN/m+/2xt3br/ubJ5euFojWhDnHUZoaqd08TCSQPd4fFCCx75MjDeCnwYMn
iKSg==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;
h=content-language:mime-version:user-agent:date:message-id:subject
:from:to:dkim-signature:arc-authentication-results;
bh=/UIFqhjCCbwBLsI4w7YY98QH6G/wxe+2W4bbMDCskjM=;
b=Wv5jt+usnSgWI96GaZWUN8/VKl1drueDpU/4gkyX/iK4d6S4CuSDjwYAc3guz/TjeW
GoKCqT30IGZoStpXQbuLry7ezXNK+Fp8MJKN2n/x5ClJWHxIsxIGlP2QC3TO8RI0P5o0
GXG9izW93q1ubkdPJFt3unSjjwSYf5XVQAZQtRm9xKjqA+lbtFbsnbjJ4wgYBURnD8ma
Qxb2xsxXDelaZvtdlzHRDn5SEkbqhcCclEYw6oRLpVQFZeYtPxcCleVybtj2owJxdaLp
7wXuo/gpYe6E2cPuS2opei8AzjEhYTNzlYXTPvaoxCCTTjfGTaPv22TeRDehuIXngSEl
Nmmw==
ARC-Authentication-Results: i=1; mx.google.com;
dkim=pass header.i=@odoo.com header.s=mail header.b=MCzhjB9b;
spf=pass (google.com: domain of soup@odoo.com designates 149.202.180.44 as permitted sender) smtp.mailfrom=soup@odoo.com;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=odoo.com
Return-Path: <soup@odoo.com>
Received: from mail2.odoo.com (mail2.odoo.com. [149.202.180.44])
by mx.google.com with ESMTPS id y4si4279200wmy.148.2018.03.05.01.19.22
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Mon, 05 Mar 2018 01:19:23 -0800 (PST)
Received-SPF: pass (google.com: domain of soup@odoo.com designates 149.202.180.44 as permitted sender) client-ip=149.202.180.44;
Authentication-Results: mx.google.com;
dkim=pass header.i=@odoo.com header.s=mail header.b=MCzhjB9b;
spf=pass (google.com: domain of soup@odoo.com designates 149.202.180.44 as permitted sender) smtp.mailfrom=soup@odoo.com;
dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=odoo.com
Received: from [10.10.31.24] (unknown [91.183.114.50])
(Authenticated sender: soup)
by mail2.odoo.com (Postfix) with ESMTPSA id 7B571A4085
for <what@odoo.com>; Mon, 5 Mar 2018 10:19:21 +0100 (CET)
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=odoo.com; s=mail;
t=1520241562; bh=L2r7Sp/vjogIdM1k8H9zDGDjnhKolsTTLLjndnFC4Jc=;
h=To:From:Subject:Date:From;
b=MCzhjB9bnsrJ3uKjq+GjujFxmtrq3fc7Vv7Vg2C72EPKnkxgqy6yPjWKtXbBlaiT3
YjKI24aiSQlOeOPQiqFgiDzeqqemNDp+CRuhoYz1Vbz+ESRaHtkWRLb7ZjvohS2k7e
RTq7tUxY2nUL2YrNHV7DFYtJVBwiTuyLP6eAiJdE=
To: what@odoo.com
From: Soup <soup@odoo.com>
Subject: =?UTF-8?Q?Soupe_du_jour_:_Pois_cass=c3=a9s?=
Message-ID: <a05d8334-7b7c-df68-c96a-4a88ed19f31b@odoo.com>
Date: Mon, 5 Mar 2018 10:19:21 +0100
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101
Thunderbird/52.6.0
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="------------1F2D18B1129FC2F0B9EECF50"
Content-Language: en-US
X-Spam-Status: No, score=-1.2 required=5.0 tests=ALL_TRUSTED,BAYES_00,
HTML_IMAGE_ONLY_08,HTML_MESSAGE,T_REMOTE_IMAGE autolearn=no
autolearn_force=no version=3.4.0
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail2.odoo.com
This is a multi-part message in MIME format.
--------------1F2D18B1129FC2F0B9EECF50
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 8bit
Résultat de recherche d'images pour "dessin la princesse au petit pois"
--
Soup
Odoo S.A.
Chaussée de Namur, 40
B-1367 Grand Rosière
Web: http://www.odoo.com
--------------1F2D18B1129FC2F0B9EECF50
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 8bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p><img
src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQjCNAadd3NDM8g9w0P_-gAVYrrqC0wmBNYKGsTZ2Pst5SsNxTRnA"
alt="Résultat de recherche d'images pour &quot;dessin la
princesse au petit pois&quot;"></p>
<pre class="moz-signature" cols="72">--
Soup
Odoo S.A.
Chaussée de Namur, 40
B-1367 Grand Rosière
Web: <a class="moz-txt-link-freetext" href="http://www.odoo.com">http://www.odoo.com</a> </pre>
</body>
</html>
--------------1F2D18B1129FC2F0B9EECF50--
--------------A6B5FD5F68F4D73ECD739009--"""
+7 -1
View File
@@ -7,7 +7,7 @@ from email.utils import formataddr
from odoo.addons.test_mail.data.test_mail_data import \
MAIL_TEMPLATE, MAIL_TEMPLATE_PLAINTEXT, MAIL_MULTIPART_MIXED, MAIL_MULTIPART_MIXED_TWO, \
MAIL_MULTIPART_IMAGE, MAIL_SINGLE_BINARY
MAIL_MULTIPART_IMAGE, MAIL_SINGLE_BINARY, MAIL_EML_ATTACHMENT
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails
from odoo.tools import mute_logger
@@ -78,6 +78,12 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
'message_id': '<123456-openerp-%s-mail.test.simple@%s>' % (cls.test_record.id, socket.gethostname()),
})
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_parse_eml(self):
""" Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed.
"""
self.env['mail.thread'].message_process('mail.channel', MAIL_EML_ATTACHMENT)
@mute_logger('odoo.addons.mail.models.mail_thread')
def test_message_process_cid(self):
record = self.format_and_process(MAIL_MULTIPART_IMAGE, to='groups@example.com')
@@ -1975,7 +1975,13 @@ eventHandler.modules.toolbar.button.updateRecentColor = function (elBtn, sEvent,
};
$(document).on('click keyup', function () {
var $popover = $((range.create()||{}).sc).closest('[contenteditable]');
var current_range = {};
try {
current_range = range.create() || {};
} catch (e) {
// if range is on Restricted element ignore error
}
var $popover = $(current_range.sc).closest('[contenteditable]');
var popover_history = ($popover.data()||{}).NoteHistory;
if (!popover_history || popover_history === history) return;
var editor = $popover.parent('.note-editor');
@@ -227,6 +227,26 @@ function imgToFont($editable) {
});
}
/*
* Utility function to apply function over descendants elements
*
* This is needed until the following issue of jQuery is solved:
* https://github.com./jquery/sizzle/issues/403
*
* @param {Element} node The root Element node
* @param {Function} func The function applied over descendants
*/
function applyOverDescendants(node, func) {
node = node.firstChild;
while (node) {
if (node.nodeType === 1) {
func(node);
applyOverDescendants(node, func);
}
node = node.nextSibling;
}
}
/**
* Converts css style to inline style (leave the classes on elements but forces
* the style they give as inline style).
@@ -237,9 +257,9 @@ function classToStyle($editable) {
if (!rulesCache.length) {
getMatchedCSSRules($editable[0]);
}
$editable.find('*').each(function () {
var $target = $(this);
var css = getMatchedCSSRules(this);
applyOverDescendants($editable[0], function (node) {
var $target = $(node);
var css = getMatchedCSSRules(node);
var style = $target.attr('style') || '';
_.each(css, function (v,k) {
if (!(new RegExp('(^|;)\s*' + k).test(style))) {
@@ -252,12 +272,12 @@ function classToStyle($editable) {
$target.attr('style', style);
}
// Apple Mail
if (this.nodeName === 'TD' && !this.childNodes.length) {
this.innerHTML = '&nbsp;';
if (node.nodeName === 'TD' && !node.childNodes.length) {
node.innerHTML = '&nbsp;';
}
// Outlook
if (this.nodeName === 'A' && $target.hasClass('btn') && !$target.children().length) {
if (node.nodeName === 'A' && $target.hasClass('btn') && !$target.children().length) {
var $hack = $('<table class="o_outlook_hack"><tr><td></td></tr></table>');
$hack.find('td')
.attr('height', $target.outerHeight())
@@ -269,7 +289,7 @@ function classToStyle($editable) {
$target.after($hack);
$target.appendTo($hack.find('td'));
// the space add a line when it's a table but it's invisible when it's a link
var node = $hack[0].previousSibling;
node = $hack[0].previousSibling;
if (node && node.nodeType === Node.TEXT_NODE && !node.textContent.match(/\S/)) {
$(node).remove();
}
@@ -297,9 +317,9 @@ function styleToClass($editable) {
var $c = $('<span/>').appendTo(document.body);
$editable.find('*').each(function () {
var $target = $(this);
var css = getMatchedCSSRules(this);
applyOverDescendants($editable[0], function (node) {
var $target = $(node);
var css = getMatchedCSSRules(node);
var style = '';
_.each(css, function (v,k) {
if (!(new RegExp('(^|;)\s*' + k).test(style))) {
+1 -1
View File
@@ -7,4 +7,4 @@ from odoo import models, fields
class ResPartner(models.Model):
_inherit = 'res.partner'
last_website_so_id = fields.Many2one('sale.order', string='Last Online Sales Order')
last_website_so_id = fields.Many2one('sale.order', string='Last Online Sales Order', copy=False)
+2 -2
View File
@@ -116,7 +116,7 @@ class TestSanitizer(unittest.TestCase):
test_data = [
(
'<span style="position: fixed; top: 0px; left: 50px; width: 40%; height: 50%; background-color: red;">Coin coin </span>',
['background-color: red', 'Coin coin'],
['background-color:red', 'Coin coin'],
['position', 'top', 'left']
), (
"""<div style='before: "Email Address; coincoin cheval: lapin";
@@ -124,7 +124,7 @@ class TestSanitizer(unittest.TestCase):
this; means: anything ?#ùµ"
; some-property: 2px; top: 3'>youplaboum</div>""",
['font-size: 30px', 'youplaboum'],
['font-size:30px', 'youplaboum'],
['some-property', 'top', 'cheval']
), (
'<span style="width">Coincoin</span>',
+1 -1
View File
@@ -510,7 +510,7 @@ REMOVE_CLASS = u"""
</div>
"""
REMOVE_CLASS_IN = [
u'<div style="font-size: 12pt; font-family: \'Times New Roman\'; color: #000000">',
u'<div style="font-size:12pt; font-family:\'Times New Roman\'; color:#000000">',
u'An error occured in a modal and I will send you back the html to try opening one on your end']
REMOVE_CLASS_OUT = [
u'<div class="modal-backdrop in">',
+2
View File
@@ -1825,6 +1825,8 @@ class Selection(Field):
def convert_to_cache(self, value, record, validate=True):
if not validate:
return value or False
if value and self.column_type[0] == 'int4':
value = int(value)
if value in self.get_values(record.env):
return value
elif not value:
+1 -1
View File
@@ -155,7 +155,7 @@ class _Cleaner(clean.Cleaner):
if style[0].lower() in self._style_whitelist:
valid_styles[style[0].lower()] = style[1]
if valid_styles:
el.attrib['style'] = '; '.join('%s: %s' % (key, val) for (key, val) in valid_styles.items())
el.attrib['style'] = '; '.join('%s:%s' % (key, val) for (key, val) in valid_styles.items())
else:
del el.attrib['style']