[MERGE] forward port branch 11.0 up to 44ea58234e
This commit is contained in:
@@ -32,6 +32,11 @@ file_filter = addons/account_budget/i18n/<lang>.po
|
||||
source_file = addons/account_budget/i18n/account_budget.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.account_cash_basis_base_account]
|
||||
file_filter = addons/account_cash_basis_base_account/i18n/<lang>.po
|
||||
source_file = addons/account_cash_basis_base_account/i18n/account_cash_basis_base_account.pot
|
||||
source_lang = en
|
||||
|
||||
[odoo-master.account_cancel]
|
||||
file_filter = addons/account_cancel/i18n/<lang>.po
|
||||
source_file = addons/account_cancel/i18n/account_cancel.pot
|
||||
|
||||
@@ -1262,6 +1262,8 @@ class AccountInvoice(models.Model):
|
||||
values[name] = line[name]
|
||||
elif name == 'invoice_line_tax_ids':
|
||||
values[name] = [(6, 0, line[name].ids)]
|
||||
elif name == 'analytic_tag_ids':
|
||||
values[name] = [(6, 0, line[name].ids)]
|
||||
result.append((0, 0, values))
|
||||
return result
|
||||
|
||||
|
||||
@@ -326,7 +326,6 @@
|
||||
<td style="padding:25px 0px;">
|
||||
<strong>Add this event to your calendar</strong>
|
||||
<a href="https://www.google.com/calendar/render?action=TEMPLATE&text=${object.event_id.name}&dates=${date_begin}/${date_end}&location=${location}" style="padding:3px 5px;border:1px solid #875A7B;color:#875A7B;text-decoration:none;border-radius:3px;" target="new"><img src="/web_editor/font_to_img/61525/rgb(135,90,123)/16" style="vertical-align:middle;" height="16"> Google</a>
|
||||
<a href="https://bay02.calendar.live.com/calendar/calendar.aspx?rru=addevent&summary=${object.event_id.name}&dtstart=${date_begin}&dtend=${date_end}&location=${location}" style="padding:3px 5px;border:1px solid #875A7B;color:#875A7B;text-decoration:none;border-radius:3px;" target="new"><img src="/web_editor/font_to_img/61525/rgb(135,90,123)/16" style="vertical-align:middle;" height="16"> Outlook</a>
|
||||
<a href="https://calendar.yahoo.com/?v=60&view=d&type=20&title=${object.event_id.name}&in_loc=${location}&st=${format_tz(object.event_id.date_begin, tz='UTC', format='%Y%m%dT%H%M%S')}&et=${format_tz(object.event_id.date_end, tz='UTC', format='%Y%m%dT%H%M%S')}" style="padding:3px 5px;border:1px solid #875A7B;color:#875A7B;text-decoration:none;border-radius:3px;" target="new"><img src="/web_editor/font_to_img/61525/rgb(135,90,123)/16" style="vertical-align:middle;" height="16"> Yahoo</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -483,6 +483,17 @@ class EventRegistration(models.Model):
|
||||
]).write({'partner_id': new_partner.id})
|
||||
return super(EventRegistration, self)._message_post_after_hook(message, values, notif_layout)
|
||||
|
||||
@api.multi
|
||||
def message_get_default_recipients(self):
|
||||
# Prioritize registration email over partner_id, which may be shared when a single
|
||||
# partner booked multiple seats
|
||||
return {
|
||||
r.id: {'partner_ids': [],
|
||||
'email_to': r.email,
|
||||
'email_cc': False}
|
||||
for r in self
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def action_send_badge_email(self):
|
||||
""" Open a window to compose an email, with the template - 'event_badge'
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<t t-if="l.task_id"><span t-field="l.task_id.name"/></t>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-esc="'%s:%02.0f' % tuple(int(round(x)) for x in divmod(l.unit_amount*60,60))"/>
|
||||
<span t-field="l.unit_amount" t-options="{'widget': 'float_time'}"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
@@ -53,7 +53,9 @@
|
||||
<td />
|
||||
<td t-if="show_task"/>
|
||||
<td class="text-right"><strong>Total</strong></td>
|
||||
<td class="text-right"><strong t-esc="'%s:%02.0f' % tuple(int(round(x)) for x in divmod(sum(docs.mapped('unit_amount'))*60,60))"/></td>
|
||||
<t t-set="total_value" t-value="sum(docs.mapped('unit_amount'))" />
|
||||
<t t-set="sign" t-value="'-' if total_value < 0 else ''"/>
|
||||
<td class="text-right"><strong t-esc="sign + '%s:%02.0f' % tuple(int(round(x)) for x in divmod(abs(total_value)*60,60))"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -63,7 +63,7 @@ def format_amount(env, amount, currency):
|
||||
lang = env['res.lang']._lang_get(env.context.get('lang') or 'en_US')
|
||||
|
||||
formatted_amount = lang.format(fmt, currency.round(amount), grouping=True, monetary=True)\
|
||||
.replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'\u2011')
|
||||
.replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}')
|
||||
|
||||
pre = post = u''
|
||||
if currency.position == 'before':
|
||||
|
||||
@@ -255,6 +255,11 @@ class PosConfig(models.Model):
|
||||
if any(self.journal_ids.mapped(lambda journal: journal.currency_id and journal.currency_id != self.currency_id)):
|
||||
raise ValidationError(_("All payment methods must be in the same currency as the Sales Journal or the company currency if that is not set."))
|
||||
|
||||
@api.constrains('company_id', 'available_pricelist_ids')
|
||||
def _check_companies(self):
|
||||
if any(self.available_pricelist_ids.mapped(lambda pl: pl.company_id.id not in (False, self.company_id.id))):
|
||||
raise ValidationError(_("The selected pricelists must belong to no company or the company of the point of sale."))
|
||||
|
||||
@api.onchange('iface_print_via_proxy')
|
||||
def _onchange_iface_print_via_proxy(self):
|
||||
self.iface_print_auto = self.iface_print_via_proxy
|
||||
|
||||
@@ -141,6 +141,7 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
}
|
||||
|
||||
// Display and animate the progress bars
|
||||
var barNumber = 0;
|
||||
_.each(self.colors, function (val, key) {
|
||||
var $bar = self.$bars[val];
|
||||
var count = self.subgroupCounts && self.subgroupCounts[key] || 0;
|
||||
@@ -165,7 +166,14 @@ var KanbanColumnProgressBar = Widget.extend({
|
||||
window.getComputedStyle($bar[0]).getPropertyValue('width'); // Force reflow so that animations work
|
||||
if (count > 0) {
|
||||
$bar.addClass('o_bar_has_records');
|
||||
// Make sure every bar that has records has some space
|
||||
// and that everything adds up to 100%
|
||||
var minWidth = 6*barNumber;
|
||||
var maxWidth = 100 - minWidth;
|
||||
self.$('.progress-bar.o_bar_has_records').css('max-width', maxWidth + '%');
|
||||
self.$('.progress-bar.o_bar_has_records').css('min-width', minWidth + '%');
|
||||
$bar.css('width', (count * 100 / self.groupCount) + '%');
|
||||
barNumber++;
|
||||
} else {
|
||||
$bar.css('width', '');
|
||||
}
|
||||
|
||||
@@ -73,11 +73,6 @@
|
||||
margin-bottom: 0;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
|
||||
// Be sure that a bar with data has a minimun width
|
||||
&.o_bar_has_records {
|
||||
min-width: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.o_kanban_counter_label {
|
||||
|
||||
@@ -76,4 +76,4 @@ class WebsiteSaleDelivery(WebsiteSale):
|
||||
lang = request.env['res.lang']._lang_get(request.env.context.get('lang') or 'en_US')
|
||||
|
||||
return lang.format(fmt, currency.round(amount), grouping=True, monetary=True)\
|
||||
.replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'\u2011')
|
||||
.replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}')
|
||||
|
||||
@@ -7,6 +7,7 @@ from odoo import api, fields, models, _
|
||||
from PIL import Image
|
||||
import babel
|
||||
from lxml import etree
|
||||
import math
|
||||
|
||||
from odoo.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils, format_date, pycompat
|
||||
|
||||
@@ -125,7 +126,7 @@ class IntegerConverter(models.AbstractModel):
|
||||
|
||||
@api.model
|
||||
def value_to_html(self, value, options):
|
||||
return pycompat.to_text(self.user_lang().format('%d', value, grouping=True).replace(r'-', u'\u2011'))
|
||||
return pycompat.to_text(self.user_lang().format('%d', value, grouping=True).replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}'))
|
||||
|
||||
|
||||
class FloatConverter(models.AbstractModel):
|
||||
@@ -145,7 +146,7 @@ class FloatConverter(models.AbstractModel):
|
||||
value = float_utils.float_round(value, precision_digits=precision)
|
||||
fmt = '%.{precision}f'.format(precision=precision)
|
||||
|
||||
formatted = self.user_lang().format(fmt, value, grouping=True).replace(r'-', u'\u2011')
|
||||
formatted = self.user_lang().format(fmt, value, grouping=True).replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}')
|
||||
|
||||
# %f does not strip trailing zeroes. %g does but its precision causes
|
||||
# it to switch to scientific notation starting at a million *and* to
|
||||
@@ -325,7 +326,7 @@ class MonetaryConverter(models.AbstractModel):
|
||||
|
||||
lang = self.user_lang()
|
||||
formatted_amount = lang.format(fmt, display_currency.round(value),
|
||||
grouping=True, monetary=True).replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'\u2011')
|
||||
grouping=True, monetary=True).replace(r' ', u'\N{NO-BREAK SPACE}').replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}')
|
||||
|
||||
pre = post = u''
|
||||
if display_currency.position == 'before':
|
||||
@@ -367,8 +368,9 @@ class FloatTimeConverter(models.AbstractModel):
|
||||
|
||||
@api.model
|
||||
def value_to_html(self, value, options):
|
||||
hours, minutes = divmod(value * 60, 60)
|
||||
return '%02d:%02d' % (hours, minutes)
|
||||
sign = math.copysign(1.0, value)
|
||||
hours, minutes = divmod(abs(value) * 60, 60)
|
||||
return '%02d:%02d' % (sign * hours, minutes)
|
||||
|
||||
|
||||
class DurationConverter(models.AbstractModel):
|
||||
|
||||
@@ -73,7 +73,7 @@ class TestFloatExport(TestBasicExport):
|
||||
converter = self.get_converter('float')
|
||||
|
||||
value = converter(-42.0)
|
||||
self.assertEqual(value, u"\u201142.0")
|
||||
self.assertEqual(value, u"-\N{ZERO WIDTH NO-BREAK SPACE}42.0")
|
||||
|
||||
value = converter(42.0100)
|
||||
self.assertEqual(value, "42.01")
|
||||
@@ -120,7 +120,7 @@ class TestCurrencyExport(TestExport):
|
||||
converted = self.convert(obj, dest=currency)
|
||||
|
||||
self.assertEqual(
|
||||
converted, u'<span class="oe_currency_value">\u20110.12</span>'
|
||||
converted, u'<span class="oe_currency_value">-\N{ZERO WIDTH NO-BREAK SPACE}0.12</span>'
|
||||
u'\N{NO-BREAK SPACE}{symbol}'.format(
|
||||
obj=obj,
|
||||
symbol=currency.symbol
|
||||
|
||||
@@ -131,7 +131,7 @@ class FSWatcher(object):
|
||||
if isinstance(event, (FileCreatedEvent, FileModifiedEvent, FileMovedEvent)):
|
||||
if not event.is_directory:
|
||||
path = getattr(event, 'dest_path', event.src_path)
|
||||
if path.endswith('.py'):
|
||||
if path.endswith('.py') and not os.path.basename(path).startswith('.~'):
|
||||
try:
|
||||
source = open(path, 'rb').read() + b'\n'
|
||||
compile(source, path, 'exec')
|
||||
|
||||
@@ -107,8 +107,8 @@ _SAFE_OPCODES = _EXPR_OPCODES.union(set(opmap[x] for x in [
|
||||
'JUMP_FORWARD', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_ABSOLUTE',
|
||||
# New in Python 2.7 - http://bugs.python.org/issue4715 :
|
||||
'JUMP_IF_FALSE_OR_POP', 'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE',
|
||||
'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'END_FINALLY', 'RAISE_VARARGS',
|
||||
'LOAD_NAME', 'STORE_NAME', 'DELETE_NAME', 'LOAD_ATTR',
|
||||
'POP_JUMP_IF_TRUE', 'SETUP_EXCEPT', 'SETUP_FINALLY', 'END_FINALLY',
|
||||
'RAISE_VARARGS', 'LOAD_NAME', 'STORE_NAME', 'DELETE_NAME', 'LOAD_ATTR',
|
||||
'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST', 'UNPACK_SEQUENCE',
|
||||
'LOAD_GLOBAL', # Only allows access to restricted globals
|
||||
] if x in opmap))
|
||||
|
||||
Reference in New Issue
Block a user