diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-06-07 11:43:07 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-06-07 11:43:07 +0200 |
| commit | 2ab7ad83618dcffd32b39e3a54ae500514444476 (patch) | |
| tree | ae4398707a5c1e126895a80429693599ba65dc17 /addons | |
| parent | e0591346a4ad4d256bc2e4e49bfa5a8e21f22f45 (diff) | |
| parent | c00df146547dca9c4dea6f39c1f0408ac84ec2d7 (diff) | |
[MERGE] forward port branch saas-11.3 up to c00df146547
Diffstat (limited to 'addons')
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 <%s>' % (object.user_id.name, object.user_id.email) or '')|safe}</field> + <field name="email_from">${(object.user_id.email and '"%s" <%s>' % (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', '<', 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 <%s>' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field> + <field name="email_from">${(object.create_uid.email and '"%s" <%s>' % (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 <%s>' % (object.create_uid.name, object.create_uid.email) or '')|safe}</field> + <field name="email_from">${(object.create_uid.email and '"%s" <%s>' % (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 <%s>' % (object.user_id.name, object.user_id.email) or '')|safe}</field> + <field name="email_from">${(object.user_id.email and '"%s" <%s>' % (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" }'/> |
