summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-06-07 11:43:07 +0200
committerChristophe Simonis <chs@odoo.com>2018-06-07 11:43:07 +0200
commit2ab7ad83618dcffd32b39e3a54ae500514444476 (patch)
treeae4398707a5c1e126895a80429693599ba65dc17 /addons
parente0591346a4ad4d256bc2e4e49bfa5a8e21f22f45 (diff)
parentc00df146547dca9c4dea6f39c1f0408ac84ec2d7 (diff)
[MERGE] forward port branch saas-11.3 up to c00df146547
Diffstat (limited to 'addons')
-rw-r--r--addons/account/data/mail_template_data.xml2
-rw-r--r--addons/account/models/account_invoice.py2
-rw-r--r--addons/account/models/company.py59
-rw-r--r--addons/account/tests/test_account_move_closed_period.py18
-rw-r--r--addons/account/tests/test_reconciliation.py49
-rw-r--r--addons/account/views/account_view.xml3
-rw-r--r--addons/account_lock/__init__.py3
-rw-r--r--addons/account_lock/__manifest__.py16
-rw-r--r--addons/account_lock/models/__init__.py3
-rw-r--r--addons/account_lock/models/res_company.py14
-rw-r--r--addons/analytic/models/analytic_account.py2
-rw-r--r--addons/barcodes/static/src/js/barcode_events.js23
-rw-r--r--addons/base_automation/i18n/tr.po2
-rw-r--r--addons/base_geolocalize/i18n/tr.po28
-rw-r--r--addons/base_import/i18n/zh_CN.po7
-rw-r--r--addons/calendar/i18n/calendar.pot11
-rw-r--r--addons/calendar/i18n/zh_CN.po5
-rw-r--r--addons/calendar/models/calendar.py10
-rw-r--r--addons/crm/i18n/tr.po4
-rw-r--r--addons/crm_project/i18n/tr.po2
-rw-r--r--addons/delivery/models/stock_picking.py3
-rw-r--r--addons/event/i18n/es.po162
-rw-r--r--addons/event/views/res_partner_views.xml1
-rw-r--r--addons/google_calendar/models/google_calendar.py4
-rw-r--r--addons/hr_holidays/report/hr_leave_report.py3
-rw-r--r--addons/hr_holidays/report/hr_leave_reports.xml5
-rw-r--r--addons/hr_holidays/views/hr_leave_views.xml7
-rw-r--r--addons/hr_timesheet/models/project.py2
-rw-r--r--addons/hw_escpos/escpos/escpos.py4
-rw-r--r--addons/l10n_de/data/res.country.state.csv32
-rw-r--r--addons/l10n_fr_certification/models/res_company.py3
-rw-r--r--addons/l10n_generic_coa/__init__.py6
-rw-r--r--addons/l10n_generic_coa/__manifest__.py1
-rw-r--r--addons/l10n_nl/data/res.country.state.csv30
-rw-r--r--addons/l10n_vn/data/res.country.state.csv126
-rw-r--r--addons/mail/i18n/tr.po2
-rw-r--r--addons/mass_mailing/i18n/tr.po2
-rw-r--r--addons/mass_mailing/models/mail_mail.py2
-rw-r--r--addons/mrp/i18n/nl.po2
-rw-r--r--addons/point_of_sale/i18n/nl.po2
-rw-r--r--addons/point_of_sale/models/pos_config.py9
-rw-r--r--addons/purchase/data/mail_template_data.xml4
-rw-r--r--addons/purchase/models/account_invoice.py4
-rw-r--r--addons/purchase_stock/models/account_invoice.py4
-rw-r--r--addons/purchase_stock/models/procurement.py6
-rw-r--r--addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py15
-rw-r--r--addons/purchase_stock/tests/test_stockvaluation.py3
-rw-r--r--addons/repair/i18n/es.po17
-rw-r--r--addons/sale/data/mail_data.xml2
-rw-r--r--addons/sale/models/product_template.py2
-rw-r--r--addons/stock/i18n/nl.po7
-rw-r--r--addons/stock/models/stock_move.py6
-rw-r--r--addons/stock/report/report_stock_forecast.py4
-rw-r--r--addons/stock/tests/test_move.py26
-rw-r--r--addons/stock_account/views/stock_account_views.xml2
-rw-r--r--addons/survey/wizard/survey_email_compose_message.py2
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js7
-rw-r--r--addons/web/static/src/scss/list_view.scss1
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js52
-rw-r--r--addons/web_editor/static/src/js/editor/summernote.js2
-rw-r--r--addons/website/controllers/main.py3
-rw-r--r--addons/website_crm_partner_assign/models/res_partner.py6
-rw-r--r--addons/website_event/static/src/js/website_event.js13
-rw-r--r--addons/website_sale/views/templates.xml2
64 files changed, 661 insertions, 200 deletions
diff --git a/addons/account/data/mail_template_data.xml b/addons/account/data/mail_template_data.xml
index f461a271452..4bb340a4f5c 100644
--- a/addons/account/data/mail_template_data.xml
+++ b/addons/account/data/mail_template_data.xml
@@ -7,7 +7,7 @@
<!--Email template -->
<record id="email_template_edi_invoice" model="mail.template">
<field name="name">Invoicing: Invoice email</field>
- <field name="email_from">${(object.user_id.email and '%s &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
+ <field name="email_from">${(object.user_id.email and '&quot;%s&quot; &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
<field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="account.model_account_invoice"/>
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 3999980159a..ebc430f0603 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -1478,8 +1478,6 @@ class AccountInvoice(models.Model):
else:
payment_method = self.env.ref('account.account_payment_method_manual_out')
journal_payment_methods = pay_journal.outbound_payment_method_ids
- if payment_method not in journal_payment_methods:
- raise UserError(_('No appropriate payment method enabled on journal %s') % pay_journal.name)
communication = self.type in ('in_invoice', 'in_refund') and self.reference or self.number
if self.origin:
diff --git a/addons/account/models/company.py b/addons/account/models/company.py
index 638b0b4bcd7..785e67e4719 100644
--- a/addons/account/models/company.py
+++ b/addons/account/models/company.py
@@ -2,10 +2,12 @@
from datetime import timedelta, datetime
import calendar
+import time
+from dateutil.relativedelta import relativedelta
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError, UserError
-from odoo.exceptions import UserError
+from odoo.tools.misc import DEFAULT_SERVER_DATE_FORMAT
from odoo.tools.float_utils import float_round, float_is_zero
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, date_utils
@@ -65,6 +67,61 @@ Best Regards,'''))
account_setup_coa_done = fields.Boolean(string='Chart of Account Checked', help="Technical field holding the status of the chart of account setup step.")
account_setup_bar_closed = fields.Boolean(string='Setup Bar Closed', help="Technical field set to True when setup bar has been closed by the user.")
+ @api.multi
+ def _check_lock_dates(self, vals):
+ '''Check the lock dates for the current companies. This can't be done in a api.constrains because we need
+ to perform some comparison between new/old values. This method forces the lock dates to be irreversible.
+
+ * You cannot define stricter conditions on advisors than on users. Then, the lock date on advisor must be set
+ after the lock date for users.
+ * You cannot lock a period that is not finished yet. Then, the lock date for advisors must be set after the
+ last day of the previous month.
+ * The new lock date for advisors must be set after the previous lock date.
+
+ :param vals: The values passed to the write method.
+ '''
+ period_lock_date = vals.get('period_lock_date') and\
+ time.strptime(vals['period_lock_date'], DEFAULT_SERVER_DATE_FORMAT)
+ fiscalyear_lock_date = vals.get('fiscalyear_lock_date') and\
+ time.strptime(vals['fiscalyear_lock_date'], DEFAULT_SERVER_DATE_FORMAT)
+
+ previous_month = datetime.strptime(fields.Date.today(), DEFAULT_SERVER_DATE_FORMAT) + relativedelta(months=-1)
+ days_previous_month = calendar.monthrange(previous_month.year, previous_month.month)
+ previous_month = previous_month.replace(day=days_previous_month[1]).timetuple()
+ for company in self:
+ old_fiscalyear_lock_date = company.fiscalyear_lock_date and\
+ time.strptime(company.fiscalyear_lock_date, DEFAULT_SERVER_DATE_FORMAT)
+
+ # The user attempts to remove the lock date for advisors
+ if old_fiscalyear_lock_date and not fiscalyear_lock_date and 'fiscalyear_lock_date' in vals:
+ raise ValidationError(_('The lock date for advisors is irreversible and can\'t be removed.'))
+
+ # The user attempts to set a lock date for advisors prior to the previous one
+ if old_fiscalyear_lock_date and fiscalyear_lock_date and fiscalyear_lock_date < old_fiscalyear_lock_date:
+ raise ValidationError(_('The new lock date for advisors must be set after the previous lock date.'))
+
+ # In case of no new fiscal year in vals, fallback to the oldest
+ if not fiscalyear_lock_date:
+ if old_fiscalyear_lock_date:
+ fiscalyear_lock_date = old_fiscalyear_lock_date
+ else:
+ continue
+
+ # The user attempts to set a lock date for advisors prior to the last day of previous month
+ if fiscalyear_lock_date > previous_month:
+ raise ValidationError(_('You cannot lock a period that is not finished yet. Please make sure that the lock date for advisors is not set after the last day of the previous month.'))
+
+ # In case of no new period lock date in vals, fallback to the one defined in the company
+ if not period_lock_date:
+ if company.period_lock_date:
+ period_lock_date = time.strptime(company.period_lock_date, DEFAULT_SERVER_DATE_FORMAT)
+ else:
+ continue
+
+ # The user attempts to set a lock date for advisors prior to the lock date for users
+ if period_lock_date < fiscalyear_lock_date:
+ raise ValidationError(_('You cannot define stricter conditions on advisors than on users. Please make sure that the lock date on advisor is set before the lock date for users.'))
+
@api.model
def _verify_fiscalyear_last_day(self, company_id, last_day, last_month):
company = self.browse(company_id)
diff --git a/addons/account/tests/test_account_move_closed_period.py b/addons/account/tests/test_account_move_closed_period.py
index 298738a0d6c..5de3e97ac4f 100644
--- a/addons/account/tests/test_account_move_closed_period.py
+++ b/addons/account/tests/test_account_move_closed_period.py
@@ -1,6 +1,8 @@
from odoo.addons.account.tests.account_test_classes import AccountingTestCase
from odoo.osv.orm import except_orm
-from datetime import datetime, timedelta
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+from calendar import monthrange
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
from odoo.tests import tagged
@@ -14,14 +16,16 @@ class TestPeriodState(AccountingTestCase):
def setUp(self):
super(TestPeriodState, self).setUp()
self.user_id = self.env.user
- self.day_before_yesterday = datetime.now() - timedelta(2)
- self.yesterday = datetime.now() - timedelta(1)
- self.yesterday_str = self.yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
+ last_day_month = datetime.now() - relativedelta(months=1)
+ last_day_month = last_day_month.replace(day=monthrange(last_day_month.year, last_day_month.month)[1])
+ self.last_day_month_str = last_day_month.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
#make sure there is no unposted entry
- draft_entries = self.env['account.move'].search([('date', '<=', self.yesterday_str), ('state', '=', 'draft')])
+ draft_entries = self.env['account.move'].search([('date', '<=', self.last_day_month_str), ('state', '=', 'draft')])
if draft_entries:
draft_entries.post()
- self.user_id.company_id.write({'fiscalyear_lock_date': self.yesterday_str})
+ self.user_id.company_id.fiscalyear_lock_date = self.last_day_month_str
self.sale_journal_id = self.env['account.journal'].search([('type', '=', 'sale')])[0]
self.account_id = self.env['account.account'].search([('internal_type', '=', 'receivable')])[0]
@@ -30,7 +34,7 @@ class TestPeriodState(AccountingTestCase):
move = self.env['account.move'].create({
'name': '/',
'journal_id': self.sale_journal_id.id,
- 'date': self.day_before_yesterday.strftime(DEFAULT_SERVER_DATE_FORMAT),
+ 'date': self.last_day_month_str,
'line_ids': [(0, 0, {
'name': 'foo',
'debit': 10,
diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py
index 9a3e816efce..814fda8c7d7 100644
--- a/addons/account/tests/test_reconciliation.py
+++ b/addons/account/tests/test_reconciliation.py
@@ -43,6 +43,12 @@ class TestReconciliation(AccountingTestCase):
self.diff_income_account = self.env['res.users'].browse(self.env.uid).company_id.income_currency_exchange_account_id
self.diff_expense_account = self.env['res.users'].browse(self.env.uid).company_id.expense_currency_exchange_account_id
+ self.inbound_payment_method = self.env['account.payment.method'].create({
+ 'name': 'inbound',
+ 'code': 'IN',
+ 'payment_type': 'inbound',
+ })
+
def create_invoice(self, type='out_invoice', invoice_amount=50, currency_id=None):
#we create an invoice in given currency
invoice = self.account_invoice_model.create({'partner_id': self.partner_agrolait_id,
@@ -711,6 +717,49 @@ class TestReconciliation(AccountingTestCase):
credit_aml.with_context(invoice_id=inv.id).remove_move_reconcile()
self.assertAlmostEquals(inv.residual, 111)
+ def test_revert_payment_and_reconcile(self):
+ payment = self.env['account.payment'].create({
+ 'payment_method_id': self.inbound_payment_method.id,
+ 'payment_type': 'inbound',
+ 'partner_type': 'customer',
+ 'partner_id': self.partner_agrolait_id,
+ 'journal_id': self.bank_journal_usd.id,
+ 'payment_date': '2018-06-04',
+ 'amount': 666,
+ })
+ payment.post()
+
+ self.assertEqual(len(payment.move_line_ids), 2)
+
+ bank_line = payment.move_line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.default_debit_account_id.id)
+ customer_line = payment.move_line_ids - bank_line
+
+ self.assertEqual(len(bank_line), 1)
+ self.assertEqual(len(customer_line), 1)
+ self.assertNotEqual(bank_line.id, customer_line.id)
+
+ self.assertEqual(bank_line.move_id.id, customer_line.move_id.id)
+ move = bank_line.move_id
+
+ # Reversing the payment's move
+ reversed_move_list = move.reverse_moves('2018-06-04')
+ self.assertEqual(len(reversed_move_list), 1)
+ reversed_move = self.env['account.move'].browse(reversed_move_list[0])
+
+ self.assertEqual(len(reversed_move.line_ids), 2)
+
+ # Testing the reconciliation matching between the move lines and their reversed counterparts
+ reversed_bank_line = reversed_move.line_ids.filtered(lambda l: l.account_id.id == self.bank_journal_usd.default_debit_account_id.id)
+ reversed_customer_line = reversed_move.line_ids - reversed_bank_line
+
+ self.assertEqual(len(reversed_bank_line), 1)
+ self.assertEqual(len(reversed_customer_line), 1)
+ self.assertNotEqual(reversed_bank_line.id, reversed_customer_line.id)
+ self.assertEqual(reversed_bank_line.move_id.id, reversed_customer_line.move_id.id)
+
+ self.assertEqual(reversed_bank_line.full_reconcile_id.id, bank_line.full_reconcile_id.id)
+ self.assertEqual(reversed_customer_line.full_reconcile_id.id, customer_line.full_reconcile_id.id)
+
def test_partial_reconcile_currencies_02(self):
####
# Day 1: Invoice Cust/001 to customer (expressed in USD)
diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml
index 6e22ba8b77a..f8ad1e50132 100644
--- a/addons/account/views/account_view.xml
+++ b/addons/account/views/account_view.xml
@@ -1179,7 +1179,8 @@
<group>
<field name="name"/>
<field name="partner_id"
- domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"/>
+ domain="['|', ('parent_id', '=', False), ('is_company', '=', True)]"
+ attrs="{'readonly': [('parent_state', '=', 'posted')]}"/>
</group>
<notebook colspan="4">
<page string="Information">
diff --git a/addons/account_lock/__init__.py b/addons/account_lock/__init__.py
new file mode 100644
index 00000000000..cde864bae21
--- /dev/null
+++ b/addons/account_lock/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import models
diff --git a/addons/account_lock/__manifest__.py b/addons/account_lock/__manifest__.py
new file mode 100644
index 00000000000..67e76b49098
--- /dev/null
+++ b/addons/account_lock/__manifest__.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+{
+ 'name' : 'Irreversible Lock Date',
+ 'version' : '1.0',
+ 'category': 'Accounting',
+ 'description': """
+ Make the lock date irreversible:
+
+ * You cannot define stricter conditions on advisors than on users. Then, the lock date on advisor must be set before the lock date for users.
+ * You cannot lock a period that is not finished yet. Then, the lock date for advisors must be set before the last day of the previous month.
+ * The new lock date for advisors must be set after the previous lock date.
+ """,
+ 'depends' : ['account'],
+ 'data': [],
+}
diff --git a/addons/account_lock/models/__init__.py b/addons/account_lock/models/__init__.py
new file mode 100644
index 00000000000..e759f74fc8d
--- /dev/null
+++ b/addons/account_lock/models/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import res_company
diff --git a/addons/account_lock/models/res_company.py b/addons/account_lock/models/res_company.py
new file mode 100644
index 00000000000..5a01046f36b
--- /dev/null
+++ b/addons/account_lock/models/res_company.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, api
+
+
+class ResCompany(models.Model):
+ _inherit = 'res.company'
+
+ @api.multi
+ def write(self, vals):
+ # fiscalyear_lock_date can't be set to a prior date
+ if 'fiscalyear_lock_date' in vals or 'period_lock_date' in vals:
+ self._check_lock_dates(vals)
+ return super(ResCompany, self).write(vals)
diff --git a/addons/analytic/models/analytic_account.py b/addons/analytic/models/analytic_account.py
index f1cd99cea5d..b84046ab9fe 100644
--- a/addons/analytic/models/analytic_account.py
+++ b/addons/analytic/models/analytic_account.py
@@ -97,6 +97,7 @@ class AccountAnalyticAccount(models.Model):
domain=domain + [('amount', '>=', 0.0)],
fields=['account_id', 'currency_id', 'amount'],
groupby=['account_id', 'currency_id'],
+ lazy=False,
)
data_credit = defaultdict(float)
for l in credit_groups:
@@ -107,6 +108,7 @@ class AccountAnalyticAccount(models.Model):
domain=domain + [('amount', '<', 0.0)],
fields=['account_id', 'currency_id', 'amount'],
groupby=['account_id', 'currency_id'],
+ lazy=False,
)
data_debit = defaultdict(float)
for l in debit_groups:
diff --git a/addons/barcodes/static/src/js/barcode_events.js b/addons/barcodes/static/src/js/barcode_events.js
index a2e5808f8be..2d602a36b97 100644
--- a/addons/barcodes/static/src/js/barcode_events.js
+++ b/addons/barcodes/static/src/js/barcode_events.js
@@ -64,12 +64,14 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
'position': 'fixed',
'top': '50%',
'transform': 'translateY(-50%)',
- 'opacity': 0,
+ 'z-index': '-1',
},
});
+ // Avoid to show autocomplete for a non appearing input
+ this.$barcodeInput.attr('autocomplete', 'off');
}
- this.__removeBarcodeField = _.debounce(this._removeBarcodeField, this.inputTimeOut);
+ this.__blurBarcodeInput = _.debounce(this._blurBarcodeInput, this.inputTimeOut);
},
handle_buffered_keys: function() {
@@ -232,7 +234,7 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
this.max_time_between_keys_in_ms);
}
// if the barcode input doesn't receive keydown for a while, remove it.
- this.__removeBarcodeField();
+ this.__blurBarcodeInput();
}
},
@@ -247,21 +249,22 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
var barcodeValue = this.$barcodeInput.val();
if (barcodeValue.match(this.regexp)) {
core.bus.trigger('barcode_scanned', barcodeValue, $(e.target).parent()[0]);
- this.$barcodeInput.val('');
+ this._blurBarcodeInput();
}
},
/**
- * Remove the temporary input created to store the barcode value.
- * If nothing happens, this input will be removed, so the focus will be lost
- * and the virtual keyboard on mobile devices will be closed.
+ * Removes the value and focus from the barcode input.
+ * If nothing happens, the focus will be lost and
+ * the virtual keyboard on mobile devices will be closed.
*
* @private
*/
- _removeBarcodeField: function () {
+ _blurBarcodeInput: function () {
if (this.$barcodeInput) {
- // Reset the value and remove from the DOM.
- this.$barcodeInput.val('').remove();
+ // Close the virtual keyboard on mobile browsers
+ // FIXME: actually we can't prevent keyboard from opening
+ this.$barcodeInput.val('').blur();
}
},
diff --git a/addons/base_automation/i18n/tr.po b/addons/base_automation/i18n/tr.po
index f943fc6fda0..f1bdb579a5d 100644
--- a/addons/base_automation/i18n/tr.po
+++ b/addons/base_automation/i18n/tr.po
@@ -123,7 +123,7 @@ msgstr "Bağlantı Modeli"
#. module: base_automation
#: model:ir.model.fields,field_description:base_automation.field_base_automation__binding_type
msgid "Binding Type"
-msgstr ""
+msgstr "Bağlantı Türü"
#. module: base_automation
#: selection:base.automation.lead.test,state:0
diff --git a/addons/base_geolocalize/i18n/tr.po b/addons/base_geolocalize/i18n/tr.po
index b858540b3b8..506cb5037a7 100644
--- a/addons/base_geolocalize/i18n/tr.po
+++ b/addons/base_geolocalize/i18n/tr.po
@@ -3,19 +3,19 @@
# * base_geolocalize
#
# Translators:
-# Martin Trigaux <mat@odoo.com>, 2017
+# Martin Trigaux, 2017
# Ahmet Altinisik <aaltinisik@altinkaya.com.tr>, 2017
# Ayhan KIZILTAN <akiziltan76@hotmail.com>, 2017
# gezgin biri <gezginbiri@hotmail.com>, 2017
-# Murat Kaplan <muratk@projetgrup.com>, 2017
# Matanat Ahmadova <ahmadova.matanat01@gmail.com>, 2017
+# Umur Akın <umura@projetgrup.com>, 2018
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 10.saas~18\n"
+"Project-Id-Version: Odoo Server saas~11.2\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-09-20 09:53+0000\n"
-"PO-Revision-Date: 2017-09-20 09:53+0000\n"
-"Last-Translator: Matanat Ahmadova <ahmadova.matanat01@gmail.com>, 2017\n"
+"POT-Creation-Date: 2018-03-22 14:10+0000\n"
+"PO-Revision-Date: 2018-03-22 14:10+0000\n"
+"Last-Translator: Umur Akın <umura@projetgrup.com>, 2018\n"
"Language-Team: Turkish (https://www.transifex.com/odoo/teams/41243/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -45,6 +45,8 @@ msgid ""
"Cannot contact geolocation servers. Please make sure that your Internet "
"connection is up and running (%s)."
msgstr ""
+"Coğrafi konumlandıma sunucusuna bağlanılamıyor. Lütfen internet bağlantınızı"
+" açık ve çalışmakta olduğundan emin olun (%s)."
#. module: base_geolocalize
#: model:ir.model,name:base_geolocalize.model_res_partner
@@ -52,21 +54,21 @@ msgid "Contact"
msgstr "Kontak"
#. module: base_geolocalize
-#: model:ir.model.fields,field_description:base_geolocalize.field_res_partner_partner_latitude
-#: model:ir.model.fields,field_description:base_geolocalize.field_res_users_partner_latitude
+#: model:ir.model.fields,field_description:base_geolocalize.field_res_partner__partner_latitude
+#: model:ir.model.fields,field_description:base_geolocalize.field_res_users__partner_latitude
msgid "Geo Latitude"
msgstr "Coğrafi Enlem"
#. module: base_geolocalize
-#: model:ir.model.fields,field_description:base_geolocalize.field_res_partner_partner_longitude
-#: model:ir.model.fields,field_description:base_geolocalize.field_res_users_partner_longitude
+#: model:ir.model.fields,field_description:base_geolocalize.field_res_partner__partner_longitude
+#: model:ir.model.fields,field_description:base_geolocalize.field_res_users__partner_longitude
msgid "Geo Longitude"
msgstr "Coğ Boylam"
#. module: base_geolocalize
#: model:ir.ui.view,arch_db:base_geolocalize.view_crm_partner_geo_form
msgid "Geolocate"
-msgstr ""
+msgstr "Yer Etiketi"
#. module: base_geolocalize
#: model:ir.ui.view,arch_db:base_geolocalize.view_crm_partner_geo_form
@@ -74,8 +76,8 @@ msgid "Geolocation"
msgstr "Coğrafi Konum"
#. module: base_geolocalize
-#: model:ir.model.fields,field_description:base_geolocalize.field_res_partner_date_localization
-#: model:ir.model.fields,field_description:base_geolocalize.field_res_users_date_localization
+#: model:ir.model.fields,field_description:base_geolocalize.field_res_partner__date_localization
+#: model:ir.model.fields,field_description:base_geolocalize.field_res_users__date_localization
msgid "Geolocation Date"
msgstr "Coğrafi Konum"
diff --git a/addons/base_import/i18n/zh_CN.po b/addons/base_import/i18n/zh_CN.po
index 0eb1389d7a7..222ed8f1b4a 100644
--- a/addons/base_import/i18n/zh_CN.po
+++ b/addons/base_import/i18n/zh_CN.po
@@ -8,13 +8,14 @@
# 苏州远鼎 <tiexinliu@126.com>, 2016
# liAnGjiA <liangjia@qq.com>, 2016
# 山西清水欧度(QQ:54773801) <54773801@qq.com>, 2018
+# John Lin <linyinhuan@139.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-22 14:10+0000\n"
"PO-Revision-Date: 2018-03-22 14:10+0000\n"
-"Last-Translator: 山西清水欧度(QQ:54773801) <54773801@qq.com>, 2018\n"
+"Last-Translator: John Lin <linyinhuan@139.com>, 2018\n"
"Language-Team: Chinese (China) (https://www.transifex.com/odoo/teams/41243/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -143,7 +144,7 @@ msgstr "十进制分隔符:"
#: code:addons/base_import/static/src/xml/base_import.xml:76
#, python-format
msgid "Defer parent/child computation"
-msgstr ""
+msgstr "推迟父/子计算"
#. module: base_import
#: model:ir.model.fields,field_description:base_import.field_base_import_import__display_name
@@ -317,7 +318,7 @@ msgid ""
"If the model uses parent/child relations, computing the"
" parent / child relation occurs on every line, and lead"
" to a slower import. Defering it can speed up import."
-msgstr ""
+msgstr "如果该模型使用父/子关系,每行都会发生父/子关系计算,并使导入更慢。推迟她能提升导入速度。"
#. module: base_import
#. openerp-web
diff --git a/addons/calendar/i18n/calendar.pot b/addons/calendar/i18n/calendar.pot
index 2ed94168f2a..ab1483026ae 100644
--- a/addons/calendar/i18n/calendar.pot
+++ b/addons/calendar/i18n/calendar.pot
@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.2\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-03-22 14:11+0000\n"
-"PO-Revision-Date: 2018-03-22 14:11+0000\n"
+"POT-Creation-Date: 2018-06-06 08:01+0000\n"
+"PO-Revision-Date: 2018-06-06 08:01+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -866,6 +866,13 @@ msgid "Meeting"
msgstr ""
#. module: calendar
+#: code:addons/calendar/models/calendar.py:933
+#: code:addons/calendar/models/calendar.py:937
+#, python-format
+msgid "Meeting '%s' starts '%s' and ends '%s'"
+msgstr ""
+
+#. module: calendar
#: model:ir.ui.view,arch_db:calendar.view_calendar_event_form
msgid "Meeting Details"
msgstr ""
diff --git a/addons/calendar/i18n/zh_CN.po b/addons/calendar/i18n/zh_CN.po
index 8e768bb2786..37ccc737772 100644
--- a/addons/calendar/i18n/zh_CN.po
+++ b/addons/calendar/i18n/zh_CN.po
@@ -13,13 +13,14 @@
# 老窦 北京 <2662059195@qq.com>, 2018
# e2f <projects@e2f.com>, 2018
# e2f_cn c5 <jarvisn@ecinnovations.com>, 2018
+# John Lin <linyinhuan@139.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-22 14:11+0000\n"
"PO-Revision-Date: 2018-03-22 14:11+0000\n"
-"Last-Translator: e2f_cn c5 <jarvisn@ecinnovations.com>, 2018\n"
+"Last-Translator: John Lin <linyinhuan@139.com>, 2018\n"
"Language-Team: Chinese (China) (https://www.transifex.com/odoo/teams/41243/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -1062,7 +1063,7 @@ msgstr "邀请向导"
#. module: calendar
#: model:ir.model.fields,field_description:calendar.field_calendar_event__is_highlighted
msgid "Is the Event Highlighted"
-msgstr ""
+msgstr "是事件高亮"
#. module: calendar
#: selection:calendar.event,byday:0
diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py
index 9ffc221555b..6ae096d7eb1 100644
--- a/addons/calendar/models/calendar.py
+++ b/addons/calendar/models/calendar.py
@@ -930,9 +930,15 @@ class Meeting(models.Model):
def _check_closing_date(self):
for meeting in self:
if meeting.start_datetime and meeting.stop_datetime and meeting.stop_datetime < meeting.start_datetime:
- raise ValidationError(_('The ending date and time cannot be earlier than the starting date and time.'))
+ raise ValidationError(
+ _('The ending date and time cannot be earlier than the starting date and time.') + '\n' +
+ _("Meeting '%s' starts '%s' and ends '%s'") % (meeting.name, meeting.start_datetime, meeting.stop_datetime)
+ )
if meeting.start_date and meeting.stop_date and meeting.stop_date < meeting.start_date:
- raise ValidationError(_('The ending date cannot be earlier than the starting date.'))
+ raise ValidationError(
+ _('The ending date cannot be earlier than the starting date.') + '\n' +
+ _("Meeting '%s' starts '%s' and ends '%s'") % (meeting.name, meeting.start_date, meeting.stop_date)
+ )
@api.onchange('start_datetime', 'duration')
def _onchange_duration(self):
diff --git a/addons/crm/i18n/tr.po b/addons/crm/i18n/tr.po
index 1dc1294b67b..503973f4250 100644
--- a/addons/crm/i18n/tr.po
+++ b/addons/crm/i18n/tr.po
@@ -1021,7 +1021,7 @@ msgstr "Atamaya Zorla"
#. module: crm
#: model:ir.ui.view,arch_db:crm.res_config_settings_view_form
msgid "Format phone numbers based on national conventions"
-msgstr ""
+msgstr "Telefon numaralarını ulusal sözleşmelere göre biçimlendirme"
#. module: crm
#: code:addons/crm/models/crm_lead.py:588
@@ -1869,7 +1869,7 @@ msgstr "Telefon"
#. module: crm
#: model:ir.model.fields,field_description:crm.field_res_config_settings__module_crm_phone_validation
msgid "Phone Formatting"
-msgstr ""
+msgstr "Telefon Biçimlendirmesi"
#. module: crm
#: code:addons/crm/models/crm_team.py:165
diff --git a/addons/crm_project/i18n/tr.po b/addons/crm_project/i18n/tr.po
index f1d4348f477..9406b7286ab 100644
--- a/addons/crm_project/i18n/tr.po
+++ b/addons/crm_project/i18n/tr.po
@@ -91,4 +91,4 @@ msgstr "Proje"
#. module: crm_project
#: model:ir.model,name:crm_project.model_crm_lead_convert2task
msgid "crm.lead.convert2task"
-msgstr ""
+msgstr "crm.lead.convert2task"
diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py
index 86517b1d0ec..908b7587c51 100644
--- a/addons/delivery/models/stock_picking.py
+++ b/addons/delivery/models/stock_picking.py
@@ -162,7 +162,8 @@ class StockPicking(models.Model):
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']
+ if res['tracking_number']:
+ self.carrier_tracking_ref = res['tracking_number']
order_currency = self.sale_id.currency_id or self.company_id.currency_id
msg = _("Shipment sent to carrier %s for shipping with tracking number %s<br/>Cost: %.2f %s") % (self.carrier_id.name, self.carrier_tracking_ref, self.carrier_price, order_currency.name)
self.message_post(body=msg)
diff --git a/addons/event/i18n/es.po b/addons/event/i18n/es.po
index f55e9ffd371..418660796de 100644
--- a/addons/event/i18n/es.po
+++ b/addons/event/i18n/es.po
@@ -206,6 +206,168 @@ msgid ""
"</div>\n"
" "
msgstr ""
+"\n"
+"<div style=\"background:#F3F5F6;color:#515166;padding:25px 0px;font-family:Arial,Helvetica,sans-serif;font-size:14px;\">\n"
+"% set date_begin = format_tz(object.event_id.date_begin, tz='UTC', format='%Y%m%dT%H%M%SZ')\n"
+"% set date_end = format_tz(object.event_id.date_end, tz='UTC', format='%Y%m%dT%H%M%SZ')\n"
+" <table style=\"width:600px;margin:5px auto;\">\n"
+" <tbody>\n"
+" <tr>\n"
+" <td>\n"
+" <a href=\"/\">\n"
+" <img src=\"/logo\" alt=\"${object.company_id.name}\" style=\"vertical-align:baseline;max-width:100px;\" />\n"
+" </a>\n"
+" </td>\n"
+" <td style=\"text-align:right;vertical-align:middle;\">\n"
+" % if 'website_url' in object.event_id and object.event_id.website_url:\n"
+" <a href=\"${object.event_id.website_url}\" style=\"background-color: #1abc9c; padding: 12px; font-weight: 12px; text-decoration: none; color: #fff; border-radius: 5px; font-size:16px;\">Ver evento</a>\n"
+" % endif\n"
+" </td>\n"
+" </tr>\n"
+" </tbody>\n"
+" </table>\n"
+" <table style=\"width:600px;margin:0px auto;background:white;border:1px solid #e1e1e1;\">\n"
+" <tbody>\n"
+" <tr>\n"
+" <td style=\"padding:15px 20px 0px 20px; font-size:16px; font-weight:300;\">\n"
+" <p style=\"font-size:16px;\">\n"
+" Hi,\n"
+" </p><p style=\"font-size:16px;\">\n"
+" Nos alegra confirmarle su registro en el evento:\n"
+" </p>\n"
+" <ul>\n"
+" <li>Evento: \n"
+" % if 'website_url' in object.event_id and object.event_id.website_url:\n"
+" <a href=\"${object.event_id.website_url}\" style=\"color:#875A7B;text-decoration:none;\">${object.event_id.name}</a>\n"
+" % else:\n"
+" <strong>${object.event_id.name}</strong>\n"
+" % endif\n"
+" </li>\n"
+" <li>Asistente: ${object.name}</li>\n"
+" </ul>\n"
+" <p style=\"font-size:16px;\">\n"
+" Nos vemos pronto,\n"
+" </p>\n"
+" <p style=\"font-size:16px; margin-bottom: 30px\">\n"
+" <i>\n"
+" -- <br/>\n"
+" % if object.event_id.organizer_id:\n"
+" ${object.event_id.organizer_id.name}\n"
+" % else:\n"
+" Los organizadores.\n"
+" % endif\n"
+" </i>\n"
+" </p>\n"
+" </td>\n"
+" </tr>\n"
+" <tr>\n"
+" <td style=\"padding:15px 20px 0px 20px;\">\n"
+" <table style=\"width:100%;border-top:1px solid #e1e1e1;\">\n"
+" <tr>\n"
+" <td style=\"padding:25px 0px;vertical-align:top;\">\n"
+" <img src=\"/web_editor/font_to_img/61555/rgb(81,81,102)/34\" style=\"padding:4px;max-width:inherit;\" height=\"34\">\n"
+" </td>\n"
+" <td style=\"padding:25px 10px 25px 10px;width:50%;line-height:20px;vertical-align:top;\">\n"
+" <div><strong>Desde</strong> ${object.event_id.date_begin_located}</div>\n"
+" <div><strong>Hasta</strong> ${object.event_id.date_end_located}</div>\n"
+" <div style=\"font-size:12px;color:#9e9e9e\"><i><strong>TZ</strong> ${object.event_id.date_tz}</i></div>\n"
+" </td>\n"
+" % if object.event_id.address_id.country_id.name:\n"
+" <td style=\"padding:25px 0px;vertical-align:top;\">\n"
+" <img src=\"/web_editor/font_to_img/61505/rgb(81,81,102)/34\" style=\"padding:4px;max-width:inherit;\" height=\"34\">\n"
+" </td>\n"
+" <td style=\"padding:30px 10px 25px 10px;width:50%;vertical-align:top;\">\n"
+" % set location = ''\n"
+" % if object.event_id.address_id.name:\n"
+" <p style=\"margin:0px 0px 0px 0px;\">${object.event_id.address_id.name}</p>\n"
+" % endif\n"
+" % if object.event_id.address_id.street:\n"
+" <p style=\"margin:0px 0px 0px 0px;\">${object.event_id.address_id.street}</p>\n"
+" % set location = object.event_id.address_id.street\n"
+" % endif\n"
+" % if object.event_id.address_id.street2:\n"
+" <p style=\"margin:0px 0px 0px 0px;\">${object.event_id.address_id.street2}</p>\n"
+" % set location = '%s, %s' % (location, object.event_id.address_id.street2)\n"
+" % endif\n"
+" <p style=\"margin:0px 0px 0px 0px;\">\n"
+" % if object.event_id.address_id.city:\n"
+" ${object.event_id.address_id.city},\n"
+" % set location = '%s, %s' % (location, object.event_id.address_id.city)\n"
+" % endif\n"
+" % if object.event_id.address_id.state_id.name:\n"
+" ${object.event_id.address_id.state_id.name},\n"
+" % set location = '%s, %s' % (location, object.event_id.address_id.state_id.name)\n"
+" % endif\n"
+" % if object.event_id.address_id.zip:\n"
+" ${object.event_id.address_id.zip}\n"
+" % set location = '%s, %s' % (location, object.event_id.address_id.zip)\n"
+" % endif\n"
+" </p>\n"
+" % if object.event_id.address_id.country_id.name:\n"
+" <p style=\"margin:0px 0px 0px 0px;\">${object.event_id.address_id.country_id.name}</p>\n"
+" % set location = '%s, %s' % (location, object.event_id.address_id.country_id.name)\n"
+" % endif\n"
+" </td>\n"
+" % endif\n"
+" </tr>\n"
+" </table>\n"
+" % if object.event_id.organizer_id:\n"
+" <table style=\"width:100%;border-top:1px solid #e1e1e1;\">\n"
+" <tr>\n"
+" <td style=\"padding:10px 0px 25px 0px;\">\n"
+" <h2 style=\"font-weight:300;margin:10px 0px\">¿Preguntas sobre este evento?</h2>\n"
+" <p>Por favor contacte con el organizador:</p>\n"
+" <ul>\n"
+" <li>${object.event_id.organizer_id.name}</li>\n"
+" % if object.event_id.organizer_id.email\n"
+" <li>Correo: <a href=\"mailto:${object.event_id.organizer_id.email}\" style=\"text-decoration:none;color:#875A7B;\">${object.event_id.organizer_id.email}</a></li>\n"
+" % endif\n"
+" % if object.event_id.organizer_id.phone\n"
+" <li>Teléfono: ${object.event_id.organizer_id.phone}</li>\n"
+" % endif\n"
+" </ul>\n"
+"\n"
+" </td>\n"
+" </tr>\n"
+" </table>\n"
+" % endif\n"
+" <table style=\"width:100%;border-top:1px solid #e1e1e1;\">\n"
+" <tr>\n"
+" <td style=\"padding:25px 0px;\">\n"
+" <strong>Añada este evento a su calendario</strong>\n"
+" <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>\n"
+" <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>\n"
+" <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>\n"
+" </td>\n"
+" </tr>\n"
+" </table>\n"
+" </td>\n"
+" </tr>\n"
+" </tbody>\n"
+" </table>\n"
+" % if object.event_id.address_id:\n"
+" <div style=\"width:598px;margin:0px auto;border-left:1px solid #dddddd;border-right:1px solid #dddddd;border-bottom:1px solid #dddddd;\">\n"
+" <a href=\"https://maps.google.com/maps?q=${location}\" target=\"new\">\n"
+" <img src=\"http://maps.googleapis.com/maps/api/staticmap?autoscale=1&size=598x200&maptype=roadmap&format=png&visual_refresh=true&markers=size:mid%7Ccolor:0xa5117d%7Clabel:%7C${location}\" style=\"vertical-align:bottom;\" />\n"
+" </a>\n"
+" </div>\n"
+" % endif\n"
+" <table style=\"width:600px;margin:0px auto;text-align:center;\">\n"
+" <tbody>\n"
+" <tr>\n"
+" <td style=\"padding-top:10px;font-size: 12px;\">\n"
+" <div>Enviado por ${object.company_id.name}</div>\n"
+" % if 'website_url' in object.event_id and object.event_id.website_url:\n"
+" <div>\n"
+" Descubra <a href=\"/event\" style=\"text-decoration:none;color:#717188;\">todos nuestros eventos</a>.\n"
+" </div>\n"
+" % endif\n"
+" </td>\n"
+" </tr>\n"
+" </tbody>\n"
+" </table>\n"
+"</div>\n"
+" "
#. module: event
#: model:mail.template,body_html:event.event_reminder
diff --git a/addons/event/views/res_partner_views.xml b/addons/event/views/res_partner_views.xml
index 8fd4b774c5e..59d096efcd1 100644
--- a/addons/event/views/res_partner_views.xml
+++ b/addons/event/views/res_partner_views.xml
@@ -5,6 +5,7 @@
<field name="name">view.res.partner.form.event.inherited</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
+ <field name="groups_id" eval="[(4, ref('event.group_event_user'))]"/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button class="oe_stat_button"
diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py
index df0df9e3388..a422438778c 100644
--- a/addons/google_calendar/models/google_calendar.py
+++ b/addons/google_calendar/models/google_calendar.py
@@ -677,7 +677,7 @@ class GoogleCalendar(models.AbstractModel):
try:
all_event_from_google = self.get_event_synchro_dict(lastSync=lastSync)
except requests.HTTPError as e:
- if e.response.code == 410: # GONE, Google is lost.
+ if e.response.status_code == 410: # GONE, Google is lost.
# we need to force the rollback from this cursor, because it locks my res_users but I need to write in this tuple before to raise.
self.env.cr.rollback()
self.env.user.write({'google_calendar_last_sync_date': False})
@@ -842,7 +842,7 @@ class GoogleCalendar(models.AbstractModel):
try:
# if already deleted from gmail or never created
recs.delete_an_event(current_event[0])
- except Exception as e:
+ except requests.exceptions.HTTPError as e:
if e.response.status_code in (401, 410,):
pass
else:
diff --git a/addons/hr_holidays/report/hr_leave_report.py b/addons/hr_holidays/report/hr_leave_report.py
index acca821f58a..5df7a3e4636 100644
--- a/addons/hr_holidays/report/hr_leave_report.py
+++ b/addons/hr_holidays/report/hr_leave_report.py
@@ -5,9 +5,10 @@ from odoo import fields, models, tools
class LeaveReport(models.Model):
-
_name = "hr.leave.report"
+ _description = 'Leave Summary / Report'
_auto = False
+ _order = "date_from DESC, employee_id"
employee_id = fields.Many2one('hr.employee', string="Employee", readonly=True)
name = fields.Char('Description', readonly=True)
diff --git a/addons/hr_holidays/report/hr_leave_reports.xml b/addons/hr_holidays/report/hr_leave_reports.xml
index 6f884671c37..5afe663eaf5 100644
--- a/addons/hr_holidays/report/hr_leave_reports.xml
+++ b/addons/hr_holidays/report/hr_leave_reports.xml
@@ -46,12 +46,13 @@
<field name="model">hr.leave.report</field>
<field name="arch" type="xml">
<tree create="0" edit="0" delete="0">
- <field name="type"/>
- <field name="name"/>
+ <field name="employee_id"/>
<field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
<field name="date_from"/>
<field name="date_to"/>
<field name="state"/>
+ <field name="type"/>
+ <field name="name"/>
</tree>
</field>
</record>
diff --git a/addons/hr_holidays/views/hr_leave_views.xml b/addons/hr_holidays/views/hr_leave_views.xml
index 30f578d81f6..658bf4bac4e 100644
--- a/addons/hr_holidays/views/hr_leave_views.xml
+++ b/addons/hr_holidays/views/hr_leave_views.xml
@@ -20,6 +20,9 @@
<field name="model">hr.leave</field>
<field name="arch" type="xml">
<search string="Search Leave">
+ <field name="employee_id"/>
+ <field name="department_id"/>
+ <field name="holiday_status_id"/>
<field name="name"/>
<filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
<filter string="Approved Leaves" domain="[('state', '=', 'validate')]" name="validated"/>
@@ -40,10 +43,6 @@
<separator/>
<filter string="My Leaves" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
<separator/>
- <field name="employee_id"/>
- <field name="department_id"/>
- <field name="holiday_status_id"/>
- <separator/>
<filter string="Late Activities" name="activities_overdue"
domain="[('activity_ids.date_deadline', '&lt;', context_today().strftime('%Y-%m-%d'))]"
help="Show all records which has next action date is before today"/>
diff --git a/addons/hr_timesheet/models/project.py b/addons/hr_timesheet/models/project.py
index 1a5ddfa0b61..6a68665dc04 100644
--- a/addons/hr_timesheet/models/project.py
+++ b/addons/hr_timesheet/models/project.py
@@ -111,7 +111,7 @@ class Task(models.Model):
@api.depends('timesheet_ids.unit_amount')
def _compute_effective_hours(self):
for task in self:
- task.effective_hours = sum(task.timesheet_ids.mapped('unit_amount'))
+ task.effective_hours = round(sum(task.timesheet_ids.mapped('unit_amount')), 2)
@api.depends('effective_hours', 'subtask_effective_hours', 'planned_hours')
def _compute_progress_hours(self):
diff --git a/addons/hw_escpos/escpos/escpos.py b/addons/hw_escpos/escpos/escpos.py
index 372b9cb4ee3..77cec6d6554 100644
--- a/addons/hw_escpos/escpos/escpos.py
+++ b/addons/hw_escpos/escpos/escpos.py
@@ -522,6 +522,10 @@ class Escpos:
# Print Code
if code:
self._raw(code)
+ # We are using type A commands
+ # So we need to add the 'NULL' character
+ # https://github.com/python-escpos/python-escpos/pull/98/files#diff-a0b1df12c7c67e38915adbe469051e2dR444
+ self._raw('\x00')
else:
raise BarcodeCodeError()
diff --git a/addons/l10n_de/data/res.country.state.csv b/addons/l10n_de/data/res.country.state.csv
index dc665baae2b..400c75156ef 100644
--- a/addons/l10n_de/data/res.country.state.csv
+++ b/addons/l10n_de/data/res.country.state.csv
@@ -1,17 +1,17 @@
"id","country_id:id","name","code"
-state_de_bw,de,"Baden-Würtenberg","BW"
-state_de_by,de,"Bayern","BY"
-state_de_be,de,"Berlin","BE"
-state_de_bb,de,"Brandenburg","BB"
-state_de_hb,de,"Bremen","HB"
-state_de_hh,de,"Hamburg","HH"
-state_de_he,de,"Hessen","HE"
-state_de_mv,de,"Mecklenburg-Vorpommern","MV"
-state_de_ni,de,"Niedersachsen","NI"
-state_de_nw,de,"Nordrhein-Westfalen","NW"
-state_de_rp,de,"Rheinland-Pfalz","RP"
-state_de_sl,de,"Saarland","SL"
-state_de_sn,de,"Sachsen","SN"
-state_de_st,de,"Sachsen-Anhalt","ST"
-state_de_sh,de,"Schleswig-Holstein","SH"
-state_de_th,de,"Thüringen","TH"
+state_de_bw,base.de,"Baden-Würtenberg","BW"
+state_de_by,base.de,"Bayern","BY"
+state_de_be,base.de,"Berlin","BE"
+state_de_bb,base.de,"Brandenburg","BB"
+state_de_hb,base.de,"Bremen","HB"
+state_de_hh,base.de,"Hamburg","HH"
+state_de_he,base.de,"Hessen","HE"
+state_de_mv,base.de,"Mecklenburg-Vorpommern","MV"
+state_de_ni,base.de,"Niedersachsen","NI"
+state_de_nw,base.de,"Nordrhein-Westfalen","NW"
+state_de_rp,base.de,"Rheinland-Pfalz","RP"
+state_de_sl,base.de,"Saarland","SL"
+state_de_sn,base.de,"Sachsen","SN"
+state_de_st,base.de,"Sachsen-Anhalt","ST"
+state_de_sh,base.de,"Schleswig-Holstein","SH"
+state_de_th,base.de,"Thüringen","TH"
diff --git a/addons/l10n_fr_certification/models/res_company.py b/addons/l10n_fr_certification/models/res_company.py
index 97aa993aa7e..f5f962f6fa3 100644
--- a/addons/l10n_fr_certification/models/res_company.py
+++ b/addons/l10n_fr_certification/models/res_company.py
@@ -30,6 +30,9 @@ class ResCompany(models.Model):
if company._is_accounting_unalterable():
sequence_fields = ['l10n_fr_secure_sequence_id']
company._create_secure_sequence(sequence_fields)
+ # fiscalyear_lock_date can't be set to a prior date
+ if 'fiscalyear_lock_date' in vals or 'period_lock_date' in vals:
+ self._check_lock_dates(vals)
return res
def _create_secure_sequence(self, sequence_fields):
diff --git a/addons/l10n_generic_coa/__init__.py b/addons/l10n_generic_coa/__init__.py
index 67dee8c60db..d417dc6807c 100644
--- a/addons/l10n_generic_coa/__init__.py
+++ b/addons/l10n_generic_coa/__init__.py
@@ -1,2 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+
+def uninstall_hook(cr, registry):
+ cr.execute(
+ "DELETE FROM ir_model_data WHERE module = 'l10n_generic_coa'"
+ )
diff --git a/addons/l10n_generic_coa/__manifest__.py b/addons/l10n_generic_coa/__manifest__.py
index 314e13f44ff..2f04d8df530 100644
--- a/addons/l10n_generic_coa/__manifest__.py
+++ b/addons/l10n_generic_coa/__manifest__.py
@@ -24,4 +24,5 @@ Install some generic chart of accounts.
'data/account_invoice_demo.xml',
],
'website': 'https://www.odoo.com/page/accounting',
+ 'uninstall_hook': 'uninstall_hook',
}
diff --git a/addons/l10n_nl/data/res.country.state.csv b/addons/l10n_nl/data/res.country.state.csv
index acde191f464..1993eac995c 100644
--- a/addons/l10n_nl/data/res.country.state.csv
+++ b/addons/l10n_nl/data/res.country.state.csv
@@ -1,16 +1,16 @@
"id","country_id:id","name","code"
-state_nl_dr,nl,"Drenthe","DR"
-state_nl_fl,nl,"Flevoland","FL"
-state_nl_fr,nl,"Friesland","FR"
-state_nl_ge,nl,"Gelderland","GE"
-state_nl_gr,nl,"Groningen","GR"
-state_nl_li,nl,"Limburg","LI"
-state_nl_nb,nl,"Noord-Brabant","NB"
-state_nl_nh,nl,"Noord-Holland","NH"
-state_nl_ov,nl,"Overijssel","OV"
-state_nl_ut,nl,"Utrecht","UT"
-state_nl_ze,nl,"Zeeland","ZE"
-state_nl_zh,nl,"Zuid-Holland","ZH"
-state_nl_bq1,nl,"Bonaire","BQ1"
-state_nl_bq2,nl,"Saba","BQ2"
-state_nl_bq3,nl,"Sint Eustatius","BQ3"
+state_nl_dr,base.nl,"Drenthe","DR"
+state_nl_fl,base.nl,"Flevoland","FL"
+state_nl_fr,base.nl,"Friesland","FR"
+state_nl_ge,base.nl,"Gelderland","GE"
+state_nl_gr,base.nl,"Groningen","GR"
+state_nl_li,base.nl,"Limburg","LI"
+state_nl_nb,base.nl,"Noord-Brabant","NB"
+state_nl_nh,base.nl,"Noord-Holland","NH"
+state_nl_ov,base.nl,"Overijssel","OV"
+state_nl_ut,base.nl,"Utrecht","UT"
+state_nl_ze,base.nl,"Zeeland","ZE"
+state_nl_zh,base.nl,"Zuid-Holland","ZH"
+state_nl_bq1,base.nl,"Bonaire","BQ1"
+state_nl_bq2,base.nl,"Saba","BQ2"
+state_nl_bq3,base.nl,"Sint Eustatius","BQ3"
diff --git a/addons/l10n_vn/data/res.country.state.csv b/addons/l10n_vn/data/res.country.state.csv
index d82912d1596..9ff4ddb0195 100644
--- a/addons/l10n_vn/data/res.country.state.csv
+++ b/addons/l10n_vn/data/res.country.state.csv
@@ -1,64 +1,64 @@
"id","country_id:id","name","code"
-state_vn_VN-44,vn,An Giang,VN-44
-state_vn_VN-57,vn,Bình Dương,VN-57
-state_vn_VN-31,vn,Bình Định,VN-31
-state_vn_VN-54,vn,Bắc Giang,VN-54
-state_vn_VN-53,vn,Bắc Kạn,VN-53
-state_vn_VN-55,vn,Bạc Liêu,VN-55
-state_vn_VN-56,vn,Bắc Ninh,VN-56
-state_vn_VN-58,vn,Bình Phước,VN-58
-state_vn_VN-43,vn,Bà Rịa - Vũng Tàu,VN-43
-state_vn_VN-40,vn,Bình Thuận,VN-40
-state_vn_VN-50,vn,Bến Tre,VN-50
-state_vn_VN-04,vn,Cao Bằng,VN-04
-state_vn_VN-59,vn,Cà Mau,VN-59
-state_vn_VN-CT,vn,TP Cần Thơ,VN-CT
-state_vn_VN-71,vn,Điện Biên,VN-71
-state_vn_VN-33,vn,Đắk Lắk,VN-33
-state_vn_VN-DN,vn,TP Đà Nẵng,VN-DN
-state_vn_VN-39,vn,Đồng Nai,VN-39
-state_vn_VN-72,vn,Đắk Nông,VN-72
-state_vn_VN-45,vn,Đồng Tháp,VN-45
-state_vn_VN-30,vn,Gia Lai,VN-30
-state_vn_VN-14,vn,Hòa Bình,VN-14
-state_vn_VN-SG,vn,TP Hồ Chí Minh,VN-SG
-state_vn_VN-61,vn,Hải Dương,VN-61
-state_vn_VN-73,vn,Hậu Giang,VN-73
-state_vn_VN-03,vn,Hà Giang,VN-03
-state_vn_VN-HN,vn,Hà Nội,VN-HN
-state_vn_VN-63,vn,Hà Nam,VN-63
-state_vn_VN-HP,vn,TP Hải Phòng,VN-HP
-state_vn_VN-23,vn,Hà Tĩnh,VN-23
-state_vn_VN-66,vn,Hưng Yên,VN-66
-state_vn_VN-47,vn,Kiên Giang,VN-47
-state_vn_VN-34,vn,Khánh Hòa,VN-34
-state_vn_VN-28,vn,Kon Tum,VN-28
-state_vn_VN-41,vn,Long An,VN-41
-state_vn_VN-02,vn,Lào Cai,VN-02
-state_vn_VN-01,vn,Lai Châu,VN-01
-state_vn_VN-35,vn,Lâm Đồng,VN-35
-state_vn_VN-09,vn,Lạng Sơn,VN-09
-state_vn_VN-22,vn,Nghệ An,VN-22
-state_vn_VN-18,vn,Ninh Bình,VN-18
-state_vn_VN-67,vn,Nam Định,VN-67
-state_vn_VN-36,vn,Ninh Thuận,VN-36
-state_vn_VN-68,vn,Phú Thọ,VN-68
-state_vn_VN-32,vn,Phú Yên,VN-32
-state_vn_VN-24,vn,Quảng Bình,VN-24
-state_vn_VN-13,vn,Quảng Ninh,VN-13
-state_vn_VN-27,vn,Quảng Nam,VN-27
-state_vn_VN-29,vn,Quảng Ngãi,VN-29
-state_vn_VN-25,vn,Quảng Trị,VN-25
-state_vn_VN-05,vn,Sơn La,VN-05
-state_vn_VN-52,vn,Sóc Trăng,VN-52
-state_vn_VN-20,vn,Thái Bình,VN-20
-state_vn_VN-46,vn,Tiền Giang,VN-46
-state_vn_VN-21,vn,Thanh Hóa,VN-21
-state_vn_VN-69,vn,Thái Nguyên,VN-69
-state_vn_VN-37,vn,Tây Ninh,VN-37
-state_vn_VN-07,vn,Tuyên Quang,VN-07
-state_vn_VN-26,vn,Thừa Thiên - Huế,VN-26
-state_vn_VN-51,vn,Trà Vinh,VN-51
-state_vn_VN-49,vn,Vĩnh Long,VN-49
-state_vn_VN-70,vn,Vĩnh Phúc,VN-70
-state_vn_VN-06,vn,Yên Bái,VN-06
+state_vn_VN-44,base.vn,An Giang,VN-44
+state_vn_VN-57,base.vn,Bình Dương,VN-57
+state_vn_VN-31,base.vn,Bình Định,VN-31
+state_vn_VN-54,base.vn,Bắc Giang,VN-54
+state_vn_VN-53,base.vn,Bắc Kạn,VN-53
+state_vn_VN-55,base.vn,Bạc Liêu,VN-55
+state_vn_VN-56,base.vn,Bắc Ninh,VN-56
+state_vn_VN-58,base.vn,Bình Phước,VN-58
+state_vn_VN-43,base.vn,Bà Rịa - Vũng Tàu,VN-43
+state_vn_VN-40,base.vn,Bình Thuận,VN-40
+state_vn_VN-50,base.vn,Bến Tre,VN-50
+state_vn_VN-04,base.vn,Cao Bằng,VN-04
+state_vn_VN-59,base.vn,Cà Mau,VN-59
+state_vn_VN-CT,base.vn,TP Cần Thơ,VN-CT
+state_vn_VN-71,base.vn,Điện Biên,VN-71
+state_vn_VN-33,base.vn,Đắk Lắk,VN-33
+state_vn_VN-DN,base.vn,TP Đà Nẵng,VN-DN
+state_vn_VN-39,base.vn,Đồng Nai,VN-39
+state_vn_VN-72,base.vn,Đắk Nông,VN-72
+state_vn_VN-45,base.vn,Đồng Tháp,VN-45
+state_vn_VN-30,base.vn,Gia Lai,VN-30
+state_vn_VN-14,base.vn,Hòa Bình,VN-14
+state_vn_VN-SG,base.vn,TP Hồ Chí Minh,VN-SG
+state_vn_VN-61,base.vn,Hải Dương,VN-61
+state_vn_VN-73,base.vn,Hậu Giang,VN-73
+state_vn_VN-03,base.vn,Hà Giang,VN-03
+state_vn_VN-HN,base.vn,Hà Nội,VN-HN
+state_vn_VN-63,base.vn,Hà Nam,VN-63
+state_vn_VN-HP,base.vn,TP Hải Phòng,VN-HP
+state_vn_VN-23,base.vn,Hà Tĩnh,VN-23
+state_vn_VN-66,base.vn,Hưng Yên,VN-66
+state_vn_VN-47,base.vn,Kiên Giang,VN-47
+state_vn_VN-34,base.vn,Khánh Hòa,VN-34
+state_vn_VN-28,base.vn,Kon Tum,VN-28
+state_vn_VN-41,base.vn,Long An,VN-41
+state_vn_VN-02,base.vn,Lào Cai,VN-02
+state_vn_VN-01,base.vn,Lai Châu,VN-01
+state_vn_VN-35,base.vn,Lâm Đồng,VN-35
+state_vn_VN-09,base.vn,Lạng Sơn,VN-09
+state_vn_VN-22,base.vn,Nghệ An,VN-22
+state_vn_VN-18,base.vn,Ninh Bình,VN-18
+state_vn_VN-67,base.vn,Nam Định,VN-67
+state_vn_VN-36,base.vn,Ninh Thuận,VN-36
+state_vn_VN-68,base.vn,Phú Thọ,VN-68
+state_vn_VN-32,base.vn,Phú Yên,VN-32
+state_vn_VN-24,base.vn,Quảng Bình,VN-24
+state_vn_VN-13,base.vn,Quảng Ninh,VN-13
+state_vn_VN-27,base.vn,Quảng Nam,VN-27
+state_vn_VN-29,base.vn,Quảng Ngãi,VN-29
+state_vn_VN-25,base.vn,Quảng Trị,VN-25
+state_vn_VN-05,base.vn,Sơn La,VN-05
+state_vn_VN-52,base.vn,Sóc Trăng,VN-52
+state_vn_VN-20,base.vn,Thái Bình,VN-20
+state_vn_VN-46,base.vn,Tiền Giang,VN-46
+state_vn_VN-21,base.vn,Thanh Hóa,VN-21
+state_vn_VN-69,base.vn,Thái Nguyên,VN-69
+state_vn_VN-37,base.vn,Tây Ninh,VN-37
+state_vn_VN-07,base.vn,Tuyên Quang,VN-07
+state_vn_VN-26,base.vn,Thừa Thiên - Huế,VN-26
+state_vn_VN-51,base.vn,Trà Vinh,VN-51
+state_vn_VN-49,base.vn,Vĩnh Long,VN-49
+state_vn_VN-70,base.vn,Vĩnh Phúc,VN-70
+state_vn_VN-06,base.vn,Yên Bái,VN-06
diff --git a/addons/mail/i18n/tr.po b/addons/mail/i18n/tr.po
index b989dc5f3bd..10f755b2fec 100644
--- a/addons/mail/i18n/tr.po
+++ b/addons/mail/i18n/tr.po
@@ -3048,7 +3048,7 @@ msgstr "Modeller"
#. module: mail
#: model:ir.model,name:mail.model_base_module_uninstall
msgid "Module Uninstallation"
-msgstr ""
+msgstr "Yüklemeyi Kaldırma Modülü"
#. module: mail
#: model:ir.ui.view,arch_db:mail.view_mail_search
diff --git a/addons/mass_mailing/i18n/tr.po b/addons/mass_mailing/i18n/tr.po
index c43c4af4b0d..03ea4cd2a45 100644
--- a/addons/mass_mailing/i18n/tr.po
+++ b/addons/mass_mailing/i18n/tr.po
@@ -667,7 +667,7 @@ msgstr "E-Posta"
#: model:ir.ui.menu,name:mass_mailing.mass_mailing_menu_root
#: model:ir.ui.view,arch_db:mass_mailing.res_config_settings_view_form
msgid "Email Marketing"
-msgstr ""
+msgstr "E-posta Pazarlama"
#. module: mass_mailing
#: model:ir.model,name:mass_mailing.model_mail_mail_statistics
diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py
index 4ec9680c639..2de2b3ec4b1 100644
--- a/addons/mass_mailing/models/mail_mail.py
+++ b/addons/mass_mailing/models/mail_mail.py
@@ -84,7 +84,7 @@ class MailMail(models.Model):
def _send_prepare_values(self, partner=None):
# TDE: temporary addition (mail was parameter) due to semi-new-API
res = super(MailMail, self)._send_prepare_values(partner)
- base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
+ base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url').rstrip('/')
if self.mailing_id and res.get('body') and res.get('email_to'):
emails = tools.email_split(res.get('email_to')[0])
email_to = emails and emails[0] or False
diff --git a/addons/mrp/i18n/nl.po b/addons/mrp/i18n/nl.po
index a9638223b4f..b179108cd7d 100644
--- a/addons/mrp/i18n/nl.po
+++ b/addons/mrp/i18n/nl.po
@@ -3604,7 +3604,7 @@ msgid ""
"link to your file."
msgstr ""
"U kan een bestand van uw computer uploaden of een internet link "
-"kopiëren/plakken die linkt naar uw bestand"
+"kopiëren/plakken die linkt naar uw bestand."
#. module: mrp
#: code:addons/mrp/models/mrp_production.py:523
diff --git a/addons/point_of_sale/i18n/nl.po b/addons/point_of_sale/i18n/nl.po
index 85cd5756e51..b04366f186e 100644
--- a/addons/point_of_sale/i18n/nl.po
+++ b/addons/point_of_sale/i18n/nl.po
@@ -3575,7 +3575,7 @@ msgid ""
"category is specified, all available products will be shown."
msgstr ""
"De kassa toont de productcategorieën standaard. Als er geen categorie is "
-"gespecificeerd worden alle producten getoond"
+"gespecificeerd worden alle producten getoond."
#. module: point_of_sale
#: model:ir.model.fields,help:point_of_sale.field_pos_config__pricelist_id
diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py
index f8fb8fb3944..15e64548aad 100644
--- a/addons/point_of_sale/models/pos_config.py
+++ b/addons/point_of_sale/models/pos_config.py
@@ -358,11 +358,12 @@ class PosConfig(models.Model):
@api.multi
def write(self, vals):
- if (self.is_posbox or vals.get('is_posbox')) and (self.iface_customer_facing_display or vals.get('iface_customer_facing_display')):
- facing_display = (self.customer_facing_display_html or vals.get('customer_facing_display_html') or '').strip()
- if not facing_display:
- vals['customer_facing_display_html'] = self._compute_default_customer_html()
result = super(PosConfig, self).write(vals)
+
+ config_display = self.filtered(lambda c: c.is_posbox and c.iface_customer_facing_display and not (c.customer_facing_display_html or '').strip())
+ if config_display:
+ super(PosConfig, config_display).write({'customer_facing_display_html': self._compute_default_customer_html()})
+
self.sudo()._set_fiscal_position()
self.sudo()._check_modules_to_install()
self.sudo()._check_groups_implied()
diff --git a/addons/purchase/data/mail_template_data.xml b/addons/purchase/data/mail_template_data.xml
index d3c075c3ff0..072342afc87 100644
--- a/addons/purchase/data/mail_template_data.xml
+++ b/addons/purchase/data/mail_template_data.xml
@@ -5,7 +5,7 @@
<!--Email template -->
<record id="email_template_edi_purchase" model="mail.template">
<field name="name">RFQ - Send by Email</field>
- <field name="email_from">${(object.create_uid.email and '%s &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
+ <field name="email_from">${(object.create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
@@ -46,7 +46,7 @@ from ${object.company_id.name}.
<!--Email template -->
<record id="email_template_edi_purchase_done" model="mail.template">
<field name="name">Purchase Order - Send by Email</field>
- <field name="email_from">${(object.create_uid.email and '%s &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
+ <field name="email_from">${(object.create_uid.email and '&quot;%s&quot; &lt;%s&gt;' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
diff --git a/addons/purchase/models/account_invoice.py b/addons/purchase/models/account_invoice.py
index ba395fd25cc..c24f2f75664 100644
--- a/addons/purchase/models/account_invoice.py
+++ b/addons/purchase/models/account_invoice.py
@@ -57,7 +57,7 @@ class AccountInvoice(models.Model):
taxes = line.taxes_id
invoice_line_tax_ids = line.order_id.fiscal_position_id.map_tax(taxes, line.product_id, line.order_id.partner_id)
invoice_line = self.env['account.invoice.line']
- date = self.date_invoice
+ date = self.date
data = {
'purchase_line_id': line.id,
'name': line.order_id.name + ': ' + line.name,
@@ -113,7 +113,7 @@ class AccountInvoice(models.Model):
def _onchange_currency_id(self):
if self.currency_id:
for line in self.invoice_line_ids.filtered(lambda r: r.purchase_line_id):
- date = self.date_invoice or fields.Date.today()
+ date = self.date or fields.Date.today()
company = self.company_id
line.price_unit = line.purchase_id.currency_id._convert(
line.purchase_line_id.price_unit, self.currency_id, company, date, round=False)
diff --git a/addons/purchase_stock/models/account_invoice.py b/addons/purchase_stock/models/account_invoice.py
index 28b7bf8bd0b..c23303533e3 100644
--- a/addons/purchase_stock/models/account_invoice.py
+++ b/addons/purchase_stock/models/account_invoice.py
@@ -57,7 +57,7 @@ class AccountInvoice(models.Model):
valuation_price_unit = po_currency._convert(
i_line.purchase_line_id.product_uom._compute_price(i_line.purchase_line_id.price_unit, i_line.uom_id),
inv.currency_id,
- company=po_company, date=inv.date_invoice, round=False,
+ company=po_company, date=inv.date, round=False,
)
stock_move_obj = self.env['stock.move']
valuation_stock_move = stock_move_obj.search([
@@ -90,7 +90,7 @@ class AccountInvoice(models.Model):
# In this condition, we have a real price-valuated product which has not yet been received
valuation_price_unit = po_currency._convert(
i_line.purchase_line_id.price_unit, inv.currency_id,
- company=po_company, date=inv.date_invoice, round=False,
+ company=po_company, date=inv.date, round=False,
)
interim_account_price = valuation_price_unit * line_quantity
diff --git a/addons/purchase_stock/models/procurement.py b/addons/purchase_stock/models/procurement.py
index af4270d090a..3d5381770fe 100644
--- a/addons/purchase_stock/models/procurement.py
+++ b/addons/purchase_stock/models/procurement.py
@@ -31,12 +31,12 @@ class ProcurementRule(models.Model):
if domain in cache:
po = cache[domain]
else:
- po = self.env['purchase.order'].search([dom for dom in domain])
+ po = self.env['purchase.order'].sudo().search([dom for dom in domain])
po = po[0] if po else False
cache[domain] = po
if not po:
vals = self._prepare_purchase_order(product_id, product_qty, product_uom, origin, values, partner)
- po = self.env['purchase.order'].create(vals)
+ po = self.env['purchase.order'].sudo().create(vals)
cache[domain] = po
elif not po.origin or origin not in po.origin.split(', '):
if po.origin:
@@ -57,7 +57,7 @@ class ProcurementRule(models.Model):
break
if not po_line:
vals = self._prepare_purchase_order_line(product_id, product_qty, product_uom, values, po, partner)
- self.env['purchase.order.line'].create(vals)
+ self.env['purchase.order.line'].sudo().create(vals)
def _get_purchase_schedule_date(self, values):
"""Return the datetime value to use as Schedule Date (``date_planned``) for the
diff --git a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py
index ab20b43b524..6443d2bf9ea 100644
--- a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py
+++ b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py
@@ -53,6 +53,7 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
'name': 'vendor bill',
'type': 'in_invoice',
'date_invoice': '2017-12-31',
+ 'date': '2017-12-31',
'account_id': account_receivable.id,
})
rslt.purchase_order_change()
@@ -77,6 +78,19 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
def test_invoice_shipment(self):
""" Tests the case into which we make the invoice first, and then receive the goods.
"""
+ # First, we delete the currency rate defined in demo data for USD on June 6th.
+ # This is mandatory to ensure consistency of the data generated by the test,
+ # as stock pickings created from a PO are by design always created for
+ # the current date (there is no way forcing this), meaning that they
+ # always use today's exchange rate for their valuation.
+ # Despite the fact we rewrite the date of the valuation moves artificially,
+ # we cannot correct the debit and credit values, since the anglosaxon
+ # entries get automatically reconciled (and you cannot modify a reconciled entry).
+ # So, we have to make sure that "today"'s rate will always be the last rate we
+ # created in order to ensure complete control of the test.
+ self.env.ref('base.rateUSDbis').unlink()
+
+
# Create a PO and an invoice for it
test_product = self.test_product_order
purchase_order = self._create_purchase(test_product)
@@ -131,6 +145,7 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
'description': 'test_invoice_shipment_refund',
'filter_refund': 'cancel',
'date': '2018-03-15',
+ 'date_invoice': '2018-03-15',
})
refund_invoice_wiz.invoice_refund()
diff --git a/addons/purchase_stock/tests/test_stockvaluation.py b/addons/purchase_stock/tests/test_stockvaluation.py
index 6cff36e7635..84ac5aedb37 100644
--- a/addons/purchase_stock/tests/test_stockvaluation.py
+++ b/addons/purchase_stock/tests/test_stockvaluation.py
@@ -203,7 +203,7 @@ class TestStockValuation(TransactionCase):
wizard.process()
# the unit price of the stock move has been updated to the latest value
- self.assertAlmostEqual(move1.price_unit, price_unit_usd_new_rate, places=2)
+ self.assertAlmostEqual(move1.price_unit, price_unit_usd_new_rate)
self.assertAlmostEqual(self.product1.stock_value, price_unit_usd_new_rate * 10, delta=0.1)
@@ -417,4 +417,3 @@ class TestStockValuationWithCOA(AccountingTestCase):
# check the anglo saxon entries
price_diff_entry = self.env['account.move.line'].search([('account_id', '=', price_diff_account.id)])
self.assertEqual(price_diff_entry.credit, 100)
-
diff --git a/addons/repair/i18n/es.po b/addons/repair/i18n/es.po
index 32b246189f6..b38c0edb03b 100644
--- a/addons/repair/i18n/es.po
+++ b/addons/repair/i18n/es.po
@@ -26,13 +26,14 @@
# Fernando San Martín Woerner <fsnmartin@gmail.com>, 2018
# José Matias Zuazo <jose.zuazo@tpsistemas.com.ar>, 2018
# Nicolás Broggi <rnbroggi@gmail.com>, 2018
+# Carlos Vásquez <carlos.vasquez@clearcorp.co.cr>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-22 14:11+0000\n"
"PO-Revision-Date: 2018-03-22 14:11+0000\n"
-"Last-Translator: Nicolás Broggi <rnbroggi@gmail.com>, 2018\n"
+"Last-Translator: Carlos Vásquez <carlos.vasquez@clearcorp.co.cr>, 2018\n"
"Language-Team: Spanish (https://www.transifex.com/odoo/teams/41243/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -97,6 +98,20 @@ msgid ""
" <p>Thank you,</p>\n"
" </data>"
msgstr ""
+"<?xml version=\"1.0\"?>\n"
+"<data>Estimado(a) ${object.partner_id.name}</p>\n"
+" <p>\n"
+" Ésta es su orden de reparación ${doc_name} <strong>${object.name}</strong>\n"
+" % if object.origin:\n"
+" (con referencia: ${object.origin} )\n"
+" % endif\n"
+" % if object.invoice_method != 'none':\n"
+" por un total de <strong>${format_amount(object.amount_total, object.pricelist_id.currency_id)}.</strong>\n"
+" % endif\n"
+" </p>\n"
+" <p>Puede responder a este correo electrónico si tiene alguna duda.</p>\n"
+" <p>Gracias,</p>\n"
+" </data>"
#. module: repair
#: model:ir.ui.view,arch_db:repair.report_repairorder
diff --git a/addons/sale/data/mail_data.xml b/addons/sale/data/mail_data.xml
index 5c9d0b6a2e5..ce8dcc651a1 100644
--- a/addons/sale/data/mail_data.xml
+++ b/addons/sale/data/mail_data.xml
@@ -55,7 +55,7 @@
<!--Email template -->
<record id="email_template_edi_sale" model="mail.template">
<field name="name">Sales Order - Send by Email</field>
- <field name="email_from">${(object.user_id.email and '%s &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
+ <field name="email_from">${(object.user_id.email and '&quot;%s&quot; &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
<field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="model_id" ref="sale.model_sale_order"/>
diff --git a/addons/sale/models/product_template.py b/addons/sale/models/product_template.py
index e1ffda478ab..79dffdd719b 100644
--- a/addons/sale/models/product_template.py
+++ b/addons/sale/models/product_template.py
@@ -24,7 +24,7 @@ class ProductTemplate(models.Model):
@api.depends('product_variant_ids.sales_count')
def _sales_count(self):
for product in self:
- product.sales_count = sum([p.sales_count for p in product.product_variant_ids])
+ product.sales_count = sum([p.sales_count for p in product.with_context(active_test=False).product_variant_ids])
@api.multi
def action_view_sales(self):
diff --git a/addons/stock/i18n/nl.po b/addons/stock/i18n/nl.po
index 90ec0e3c55a..1541859ac42 100644
--- a/addons/stock/i18n/nl.po
+++ b/addons/stock/i18n/nl.po
@@ -4454,7 +4454,8 @@ msgstr "Aantallen"
#, python-format
msgid "Quants cannot be created for consumables or services."
msgstr ""
-"Quants kunnen niet worden aangemaakt voor verbruiksartikelen of diensten."
+"Hoeveelheden kunnen niet worden aangemaakt voor verbruiksartikelen of "
+"diensten."
#. module: stock
#: model:ir.model.fields,field_description:stock.field_stock_picking_type__rate_picking_backorders
@@ -5429,8 +5430,8 @@ msgid ""
"Technical field used to compute whether the mark as todo button should be "
"shown."
msgstr ""
-"Technisch veld waarmee berekend wordt of de markeer als ToDo zichtbaar mag "
-"zijn."
+"Technisch veld waarmee berekend wordt of de markeer als \"Te doen\" "
+"zichtbaar mag zijn."
#. module: stock
#: model:ir.model.fields,help:stock.field_stock_picking__show_validate
diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 533deff87af..627d503069a 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -923,11 +923,15 @@ class StockMove(models.Model):
if not move.move_orig_ids:
if move.procure_method == 'make_to_order':
continue
+ # If we don't need any quantity, consider the move assigned.
+ need = move.product_qty - move.reserved_availability
+ if float_is_zero(need, precision_rounding=move.product_id.uom_id.rounding):
+ assigned_moves |= move
+ continue
# Reserve new quants and create move lines accordingly.
available_quantity = self.env['stock.quant']._get_available_quantity(move.product_id, move.location_id)
if available_quantity <= 0:
continue
- need = move.product_qty - move.reserved_availability
taken_quantity = move._update_reserved_quantity(need, available_quantity, move.location_id, strict=False)
if float_is_zero(taken_quantity, precision_rounding=move.product_id.uom_id.rounding):
continue
diff --git a/addons/stock/report/report_stock_forecast.py b/addons/stock/report/report_stock_forecast.py
index 700aa5455de..1ca3b1195b5 100644
--- a/addons/stock/report/report_stock_forecast.py
+++ b/addons/stock/report/report_stock_forecast.py
@@ -66,7 +66,7 @@ class ReportStockForecat(models.Model):
LEFT JOIN
stock_location source_location ON sm.location_id = source_location.id
WHERE
- sm.state IN ('confirmed','assigned','waiting') and
+ sm.state IN ('confirmed','partially_available','assigned','waiting') and
source_location.usage != 'internal' and dest_location.usage = 'internal'
GROUP BY sm.date_expected,sm.product_id, sm.company_id
UNION ALL
@@ -88,7 +88,7 @@ class ReportStockForecat(models.Model):
LEFT JOIN
stock_location dest_location ON sm.location_dest_id = dest_location.id
WHERE
- sm.state IN ('confirmed','assigned','waiting') and
+ sm.state IN ('confirmed','partially_available','assigned','waiting') and
source_location.usage = 'internal' and dest_location.usage != 'internal'
GROUP BY sm.date_expected,sm.product_id, sm.company_id)
as MAIN
diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py
index 1692989f60e..28861355303 100644
--- a/addons/stock/tests/test_move.py
+++ b/addons/stock/tests/test_move.py
@@ -1074,6 +1074,31 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.customer_location), 12.0)
self.assertEqual(len(self.env['stock.quant']._gather(self.product2, self.customer_location)), 12)
+ def test_availability_8(self):
+ """ Test the assignment mechanism when the product quantity is decreased on a partially
+ reserved stock move.
+ """
+ # make some stock
+ self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 3.0)
+ self.assertAlmostEqual(self.product1.qty_available, 3.0)
+
+ move_partial = self.env['stock.move'].create({
+ 'name': 'test_partial',
+ 'location_id': self.stock_location.id,
+ 'location_dest_id': self.customer_location.id,
+ 'product_id': self.product1.id,
+ 'product_uom': self.uom_unit.id,
+ 'product_uom_qty': 5.0,
+ })
+
+ move_partial._action_confirm()
+ move_partial._action_assign()
+ self.assertAlmostEqual(self.product1.virtual_available, -2.0)
+ self.assertEqual(move_partial.state, 'partially_available')
+ move_partial.product_uom_qty = 3.0
+ move_partial._action_assign()
+ self.assertEqual(move_partial.state, 'assigned')
+
def test_unreserve_1(self):
""" Check that unreserving a stock move sets the products reserved as available and
set the state back to confirmed.
@@ -3909,4 +3934,3 @@ class StockMove(TransactionCase):
picking.button_validate()
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.customer_location), 2)
-
diff --git a/addons/stock_account/views/stock_account_views.xml b/addons/stock_account/views/stock_account_views.xml
index 656d6ea7364..77747cdea5c 100644
--- a/addons/stock_account/views/stock_account_views.xml
+++ b/addons/stock_account/views/stock_account_views.xml
@@ -44,7 +44,7 @@
<field name="model">stock.return.picking</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='product_return_moves']/tree" position="inside">
- <field name="to_refund" widget="boolean_toggle"/>
+ <field name="to_refund"/>
</xpath>
</field>
</record>
diff --git a/addons/survey/wizard/survey_email_compose_message.py b/addons/survey/wizard/survey_email_compose_message.py
index f23d836bc30..80884543585 100644
--- a/addons/survey/wizard/survey_email_compose_message.py
+++ b/addons/survey/wizard/survey_email_compose_message.py
@@ -87,8 +87,6 @@ class SurveyMailComposeMessage(models.TransientModel):
#set url
url = wizard.survey_id.public_url
- url = urls.url_parse(url).path[1:] # dirty hack to avoid incorrect urls
-
if token:
url = url + '/' + token
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 6cb91d152d0..cf3c08128ce 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -1990,9 +1990,7 @@ var BasicModel = AbstractModel.extend({
var records = [];
var ids = [];
list = this._applyX2ManyOperations(list);
- if (_.isEmpty(list.data)) {
- return $.when();
- }
+
_.each(list.data, function (localId) {
var record = self.localData[localId];
var data = record._changes || record.data;
@@ -2003,6 +2001,9 @@ var BasicModel = AbstractModel.extend({
ids.push(many2oneRecord.res_id);
model = many2oneRecord.model;
});
+ if (!ids.length) {
+ return $.when();
+ }
return this._rpc({
model: model,
method: 'name_get',
diff --git a/addons/web/static/src/scss/list_view.scss b/addons/web/static/src/scss/list_view.scss
index 49cba9409cc..539b5685a31 100644
--- a/addons/web/static/src/scss/list_view.scss
+++ b/addons/web/static/src/scss/list_view.scss
@@ -83,7 +83,6 @@
padding: 0px;
background-style: none;
border-style: none;
- height: 0px;
display: table-cell;
}
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 913ca896b7c..b9aa2d30253 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -2551,6 +2551,58 @@ QUnit.module('relational_fields', {
QUnit.module('FieldOne2Many');
+ QUnit.test('New record with a o2m also with 2 new records, ordered, and resequenced', function (assert) {
+ assert.expect(3);
+
+ // Needed to have two new records in a single stroke
+ this.data.partner.onchanges = {
+ foo: function(obj) {
+ obj.p = [
+ [5],
+ [0, 0, {trululu: false}],
+ [0, 0, {trululu: false}],
+ ]
+ }
+ };
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch:'<form string="Partners">' +
+ '<field name="foo" />' +
+ '<field name="p">' +
+ '<tree editable="bottom" default_order="int_field">' +
+ '<field name="int_field" widget="handle"/>' +
+ '<field name="trululu"/>' +
+ '</tree>' +
+ '</field>' +
+ '</form>',
+ viewOptions: {
+ mode: 'create',
+ },
+ mockRPC: function (route, args) {
+ assert.step(args.method + ' ' + args.model)
+ return this._super(route, args);
+ },
+ });
+
+ // change the int_field through drag and drop
+ // that way, we'll trigger the sorting and the name_get
+ // of the lines of "p"
+ testUtils.dragAndDrop(
+ form.$('.ui-sortable-handle').eq(1),
+ form.$('tbody tr').first(),
+ {position: 'top'}
+ );
+
+ // Only those two should have been called
+ // name_get on trululu would trigger an traceback
+ assert.verifySteps(['default_get partner', 'onchange partner']);
+
+ form.destroy();
+ });
+
QUnit.test('O2M List with pager, decoration and default_order: add and cancel adding', function (assert) {
assert.expect(3);
diff --git a/addons/web_editor/static/src/js/editor/summernote.js b/addons/web_editor/static/src/js/editor/summernote.js
index b3e06fa1716..657a8bbac8c 100644
--- a/addons/web_editor/static/src/js/editor/summernote.js
+++ b/addons/web_editor/static/src/js/editor/summernote.js
@@ -2255,7 +2255,7 @@ $.summernote.pluginEvents.backColor = function (event, editor, layoutInfo, backC
};
options.onCreateLink = function (sLinkUrl) {
- if (sLinkUrl.indexOf('mailto:') === 0) {
+ if (sLinkUrl.indexOf('mailto:') === 0 || sLinkUrl.indexOf('tel:') === 0) {
// pass
} else if (sLinkUrl.indexOf('@') !== -1 && sLinkUrl.indexOf(':') === -1) {
sLinkUrl = 'mailto:' + sLinkUrl;
diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py
index 62d862afe35..ead68c28274 100644
--- a/addons/website/controllers/main.py
+++ b/addons/website/controllers/main.py
@@ -18,8 +18,9 @@ from odoo import http, models, fields, _
from odoo.http import request
from odoo.tools import pycompat, OrderedSet
from odoo.addons.http_routing.models.ir_http import slug, _guess_mimetype
-from odoo.addons.web.controllers.main import WebClient, Binary, Home
+from odoo.addons.web.controllers.main import WebClient, Binary
from odoo.addons.portal.controllers.portal import pager as portal_pager
+from odoo.addons.portal.controllers.web import Home
logger = logging.getLogger(__name__)
diff --git a/addons/website_crm_partner_assign/models/res_partner.py b/addons/website_crm_partner_assign/models/res_partner.py
index aa0d40af98e..9b47a9f791d 100644
--- a/addons/website_crm_partner_assign/models/res_partner.py
+++ b/addons/website_crm_partner_assign/models/res_partner.py
@@ -34,11 +34,11 @@ class ResPartnerActivation(models.Model):
class ResPartner(models.Model):
_inherit = "res.partner"
- partner_weight = fields.Integer('Level Weight', default=lambda *args: 0,
+ partner_weight = fields.Integer('Level Weight', default=0, track_visibility='onchange',
help="Gives the probability to assign a lead to this partner. (0 means no assignation.)")
- grade_id = fields.Many2one('res.partner.grade', 'Level')
+ grade_id = fields.Many2one('res.partner.grade', 'Level', track_visibility='onchange')
grade_sequence = fields.Integer(related='grade_id.sequence', readonly=True, store=True)
- activation = fields.Many2one('res.partner.activation', 'Activation', index=True)
+ activation = fields.Many2one('res.partner.activation', 'Activation', index=True, track_visibility='onchange')
date_partnership = fields.Date('Partnership Date')
date_review = fields.Date('Latest Partner Review')
date_review_next = fields.Date('Next Partner Review')
diff --git a/addons/website_event/static/src/js/website_event.js b/addons/website_event/static/src/js/website_event.js
index 3aa8691fca7..ebe38118559 100644
--- a/addons/website_event/static/src/js/website_event.js
+++ b/addons/website_event/static/src/js/website_event.js
@@ -20,8 +20,11 @@ return instance.appendTo($form).then(function () {
odoo.define('website_event.website_event', function (require) {
var ajax = require('web.ajax');
+var core = require('web.core');
var Widget = require('web.Widget');
+var _t = core._t;
+
// Catch registration form event, because of JS for attendee details
var EventRegistrationForm = Widget.extend({
start: function () {
@@ -31,7 +34,6 @@ var EventRegistrationForm = Widget.extend({
.off('click')
.removeClass('a-submit')
.click(function (ev) {
- $(this).attr('disabled', true);
self.on_click(ev);
});
});
@@ -43,15 +45,18 @@ var EventRegistrationForm = Widget.extend({
var $form = $(ev.currentTarget).closest('form');
var $button = $(ev.currentTarget).closest('[type="submit"]');
var post = {};
+ $('#registration_form table').siblings('.alert').remove();
$('#registration_form select').each(function () {
post[$(this).attr('name')] = $(this).val();
});
var tickets_ordered = _.some(_.map(post, function (value, key) { return parseInt(value); }));
if (!tickets_ordered) {
- return $('#registration_form table').after(
- '<div class="alert alert-info">Please select at least one ticket.</div>'
- );
+ $('<div class="alert alert-info"/>')
+ .text(_t('Please select at least one ticket.'))
+ .insertAfter('#registration_form table');
+ return $.Deferred();
} else {
+ $button.attr('disabled', true);
return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) {
var $modal = $(modal);
$modal.find('.modal-body > div').removeClass('container'); // retrocompatibility - REMOVE ME in master / saas-19
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 21d22a91e95..e446c0b869f 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -427,7 +427,7 @@
<div id="o-carousel-product" class="carousel slide" data-ride="carousel" data-interval="0">
<div class="carousel-outer">
<div class="carousel-inner">
- <div t-if="variant_img" class="item active" itemprop="image" t-field="product.product_variant_id.image" t-options="{'widget': 'image', 'class': 'product_detail_img js_variant_img', 'alt-field': 'name', 'zoom': 'image', 'unique': product['__last_update'] + (product.product_variant_id['__last_update'] or '')}"/>
+ <div t-if="variant_img" class="item active" itemprop="image" t-field="product[:1].product_variant_id.image" t-options="{'widget': 'image', 'class': 'product_detail_img js_variant_img', 'alt-field': 'name', 'zoom': 'image', 'unique': product['__last_update'] + (product.product_variant_id['__last_update'] or '')}"/>
<div t-attf-class="item#{'' if variant_img else ' active'}" itemprop="image" t-field="product.image" t-options="{'widget': 'image', 'class': 'product_detail_img', 'alt-field': 'name', 'zoom': 'image', 'unique': product['__last_update']}"/>
<t t-if="len(image_ids)" t-foreach="image_ids" t-as="pimg">
<div class="item" t-field="pimg.image" t-options='{"widget": "image", "class": "product_detail_img", "alt-field": "name", "zoom": "image" }'/>