diff --git a/.tx/config b/.tx/config index 7d4a9e9a66b..a8680d98182 100644 --- a/.tx/config +++ b/.tx/config @@ -908,8 +908,12 @@ file_filter = addons/website_slides/i18n/.po source_file = addons/website_slides/i18n/website_slides.pot source_lang = en +[odoo-master.website_theme_install] +file_filter = addons/website_theme_install/i18n/.po +source_file = addons/website_theme_install/i18n/website_theme_install.pot +source_lang = en + [odoo-master.website_twitter] file_filter = addons/website_twitter/i18n/.po source_file = addons/website_twitter/i18n/website_twitter.pot source_lang = en - diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index de6394b2ea2..189e7a3f832 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -342,7 +342,7 @@ class AccountBankStatement(models.Model): class AccountBankStatementLine(models.Model): _name = "account.bank.statement.line" _description = "Bank Statement Line" - _order = "statement_id desc, sequence" + _order = "statement_id desc, sequence, id desc" _inherit = ['ir.needaction_mixin'] name = fields.Char(string='Label', required=True) @@ -951,8 +951,23 @@ class AccountBankStatementLine(models.Model): aml_dict['currency_id'] = statement_currency.id # Create write-offs + # When we register a payment on an invoice, the write-off line contains the amount + # currency if all related invoices have the same currency. We apply the same logic in + # the manual reconciliation. + counterpart_aml = self.env['account.move.line'] + for aml_dict in counterpart_aml_dicts: + counterpart_aml |= aml_dict.get('move_line', self.env['account.move.line']) + new_aml_currency = False + if counterpart_aml\ + and len(counterpart_aml.mapped('currency_id')) == 1\ + and counterpart_aml[0].currency_id\ + and counterpart_aml[0].currency_id != company_currency: + new_aml_currency = counterpart_aml[0].currency_id for aml_dict in new_aml_dicts: aml_dict['payment_id'] = payment and payment.id or False + if new_aml_currency and not aml_dict.get('currency_id'): + aml_dict['currency_id'] = new_aml_currency.id + aml_dict['amount_currency'] = company_currency.with_context(ctx).compute(aml_dict['debit'] - aml_dict['credit'], new_aml_currency) aml_obj.with_context(check_move_validity=False, apply_taxes=True).create(aml_dict) # Create counterpart move lines and reconcile them diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index e825ebc4ac1..e638c5aae7a 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -914,6 +914,8 @@ class AccountMoveLine(models.Model): first_line_dict['account_id'] = self[0].account_id.id if 'analytic_account_id' in first_line_dict: del first_line_dict['analytic_account_id'] + if 'tax_ids' in first_line_dict: + del first_line_dict['tax_ids'] # Writeoff line in specified writeoff account second_line_dict = vals.copy() @@ -1216,7 +1218,7 @@ class AccountMoveLine(models.Model): """ Prepare the values used to create() an account.analytic.line upon validation of an account.move.line having an analytic account. This method is intended to be extended in other modules. """ - amount = (self.credit or 0.0) - (self.debit or 0.0) + amount = (self.debit or 0.0) - (self.credit or 0.0) return { 'name': self.name, 'date': self.date, diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index d957f7c9f5a..bd40fa8816a 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -243,7 +243,7 @@ class TestReconciliation(AccountingTestCase): self.check_results(bank_stmt.move_line_ids, { self.account_euro.id: {'debit': 40.0, 'credit': 0.0, 'amount_currency': 0, 'currency_id': False}, self.account_rcv.id: {'debit': 0.0, 'credit': 32.7, 'amount_currency': -41.97, 'currency_id': self.currency_usd_id, 'currency_diff': 0, 'amount_currency_diff': -8.03}, - self.diff_income_account.id: {'debit': 0.0, 'credit': 7.3, 'amount_currency': 0, 'currency_id': False}, + self.diff_income_account.id: {'debit': 0.0, 'credit': 7.3, 'amount_currency': -9.37, 'currency_id': self.currency_usd_id}, }) # The invoice should be paid, as the payments totally cover its total diff --git a/addons/account_asset/models/account_asset.py b/addons/account_asset/models/account_asset.py index 942c54dfdf1..144f0cc518e 100644 --- a/addons/account_asset/models/account_asset.py +++ b/addons/account_asset/models/account_asset.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import calendar from datetime import date, datetime from dateutil.relativedelta import relativedelta from odoo import api, fields, models, _ from odoo.exceptions import UserError, ValidationError from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DF -from odoo.tools import float_compare +from odoo.tools import float_compare, float_is_zero class AccountAssetCategory(models.Model): @@ -156,14 +157,26 @@ class AccountAssetAsset(models.Model): if self.prorata: amount = amount_to_depr / self.method_number if sequence == 1: - days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1 - amount = (amount_to_depr / self.method_number) / total_days * days + if self.method_period % 12 != 0: + date = datetime.strptime(self.date, '%Y-%m-%d') + month_days = calendar.monthrange(date.year, date.month)[1] + days = month_days - date.day + 1 + amount = (amount_to_depr / self.method_number) / month_days * days + else: + days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1 + amount = (amount_to_depr / self.method_number) / total_days * days elif self.method == 'degressive': amount = residual_amount * self.method_progress_factor if self.prorata: if sequence == 1: - days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1 - amount = (residual_amount * self.method_progress_factor) / total_days * days + if self.method_period % 12 != 0: + date = datetime.strptime(self.date, '%Y-%m-%d') + month_days = calendar.monthrange(date.year, date.month)[1] + days = month_days - date.day + 1 + amount = (residual_amount * self.method_progress_factor) / month_days * days + else: + days = (self.company_id.compute_fiscalyear_dates(depreciation_date)['date_to'] - depreciation_date).days + 1 + amount = (residual_amount * self.method_progress_factor) / total_days * days return amount def _compute_board_undone_dotation_nb(self, depreciation_date, total_days): @@ -216,6 +229,8 @@ class AccountAssetAsset(models.Model): sequence = x + 1 amount = self._compute_board_amount(sequence, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date) amount = self.currency_id.round(amount) + if float_is_zero(amount, precision_rounding=self.currency_id.rounding): + continue residual_amount -= amount vals = { 'amount': amount, @@ -413,7 +428,7 @@ class AccountAssetAsset(models.Model): 'res_model': 'account.move', 'view_id': False, 'type': 'ir.actions.act_window', - 'domain': [('id', 'in', move_ids)] + 'domain': [('id', 'in', move_ids)], } diff --git a/addons/account_asset/tests/test_account_asset.py b/addons/account_asset/tests/test_account_asset.py index 8ef0bf56ef9..bbc36ac4dbb 100644 --- a/addons/account_asset/tests/test_account_asset.py +++ b/addons/account_asset/tests/test_account_asset.py @@ -55,7 +55,7 @@ class TestAccountAsset(common.TransactionCase): asset_modify_number_0.with_context({'active_id': account_asset_asset_office0.id}).modify() # I check the proper depreciation lines created. - self.assertEqual(account_asset_asset_office0.method_number, len(account_asset_asset_office0.depreciation_line_ids) - 1) + self.assertEqual(account_asset_asset_office0.method_number, len(account_asset_asset_office0.depreciation_line_ids)) # I compute a asset on period. context = { "active_ids": [self.ref("account_asset.menu_asset_depreciation_confirmation_wizard")], diff --git a/addons/account_voucher/models/account_voucher.py b/addons/account_voucher/models/account_voucher.py index d677a6490e5..ae3fb6eaf07 100644 --- a/addons/account_voucher/models/account_voucher.py +++ b/addons/account_voucher/models/account_voucher.py @@ -345,6 +345,20 @@ class AccountVoucherLine(models.Model): taxes = self.tax_ids.compute_all(self.price_unit, self.voucher_id.currency_id, self.quantity, product=self.product_id, partner=self.voucher_id.partner_id) self.price_subtotal = taxes['total_excluded'] + @api.onchange('product_id', 'voucher_id', 'price_unit', 'company_id') + def _onchange_line_details(self): + if not self.voucher_id or not self.product_id or not self.voucher_id.partner_id: + return + onchange_res = self.product_id_change( + self.product_id.id, + self.voucher_id.partner_id.id, + self.price_unit, + self.company_id.id, + self.voucher_id.currency_id.id, + self.voucher_id.voucher_type) + for fname, fvalue in onchange_res['values'].iteritems(): + setattr(self, fname, fvalue) + def _get_account(self, product, fpos, type): accounts = product.product_tmpl_id.get_product_accounts(fpos) if type == 'sale': @@ -353,6 +367,7 @@ class AccountVoucherLine(models.Model): @api.multi def product_id_change(self, product_id, partner_id=False, price_unit=False, company_id=None, currency_id=None, type=None): + # TDE note: mix of old and new onchange badly written in 9, multi but does not use record set context = self._context company_id = company_id if company_id is not None else context.get('company_id', False) company = self.env['res.company'].browse(company_id) diff --git a/addons/account_voucher/views/account_voucher_views.xml b/addons/account_voucher/views/account_voucher_views.xml index 2bdf99ec1e9..15c2d0a1749 100644 --- a/addons/account_voucher/views/account_voucher_views.xml +++ b/addons/account_voucher/views/account_voucher_views.xml @@ -100,8 +100,7 @@ account.voucher.line - + @@ -248,8 +247,7 @@ - + @@ -365,8 +363,7 @@ - + diff --git a/addons/auth_ldap/README.rst b/addons/auth_ldap/README.rst index 5e43727ca6f..f31047def21 100644 --- a/addons/auth_ldap/README.rst +++ b/addons/auth_ldap/README.rst @@ -8,7 +8,7 @@ will automatically create Odoo users for them on the fly. Configuration: -------------- After installing this module, you need to configure the LDAP parameters in the -Configuration tab of the Company details. Different companies may have different +General Settings menu. Different companies may have different LDAP servers, as long as they have unique usernames (usernames need to be unique in Odoo, even across multiple companies). @@ -52,7 +52,7 @@ encrypted. User Template: -------------- -In the LDAP configuration on the Company form, it is possible to select a *User +In the LDAP configuration on the General Settings, it is possible to select a *User Template*. If set, this user will be used as template to create the local users whenever someone authenticates for the first time via LDAP authentication. This allows pre-setting the default groups and menus of the first-time users. diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 3575f36f655..7d881187fe0 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -348,6 +348,9 @@ core.form_tag_registry FavoriteMenu.include({ start: function () { var self = this; + if(this.action_id === undefined) { + return this._super(); + } var am = this.findAncestor(function (a) { return a instanceof ActionManager; }); diff --git a/addons/bus/static/src/js/bus.js b/addons/bus/static/src/js/bus.js index 2ffdab84420..a79c7500669 100644 --- a/addons/bus/static/src/js/bus.js +++ b/addons/bus/static/src/js/bus.js @@ -19,6 +19,7 @@ bus.Bus = Widget.extend({ this._super(); this.options = {}; this.activated = false; + this.bus_id = _.uniqueId('bus'); this.channels = []; this.last = 0; this.stop = false; @@ -33,15 +34,24 @@ bus.Bus = Widget.extend({ this.trigger('window_focus', this.is_master); } }); - $(window).on("focus", _.bind(this.focus_change, this, true)); - $(window).on("blur", _.bind(this.focus_change, this, false)); - $(window).on("unload", _.bind(this.focus_change, this, false)); + $(window).on("focus." + this.bus_id, _.bind(this.focus_change, this, true)); + $(window).on("blur." + this.bus_id, _.bind(this.focus_change, this, false)); + $(window).on("unload." + this.bus_id, _.bind(this.focus_change, this, false)); _.each('click,keydown,keyup'.split(','), function(evtype) { - $(window).on(evtype, function() { + $(window).on(evtype + "." + self.bus_id, function() { self.last_presence = new Date().getTime(); }); }); }, + destroy: function () { + var self = this; + $(window).off("focus." + this.bus_id); + $(window).off("blur." + this.bus_id); + $(window).off("unload." + this.bus_id); + _.each('click,keydown,keyup'.split(','), function(evtype) { + $(window).off(evtype + "." + self.bus_id); + }); + }, start_polling: function(){ if(!this.activated){ this.poll(); diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index f5a315ca399..4ce72cefef6 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -78,7 +78,7 @@ class StockPicking(models.Model): def _compute_shipping_weight(self): self.shipping_weight = self.weight_bulk + sum([pack.shipping_weight for pack in self.package_ids]) - carrier_price = fields.Float(string="Shipping Cost", readonly=True) + carrier_price = fields.Float(string="Shipping Cost") delivery_type = fields.Selection(related='carrier_id.delivery_type', readonly=True) carrier_id = fields.Many2one("delivery.carrier", string="Carrier") volume = fields.Float(copy=False) @@ -90,6 +90,11 @@ class StockPicking(models.Model): weight_bulk = fields.Float('Bulk Weight', compute='_compute_bulk_weight') shipping_weight = fields.Float("Weight for Shipping", compute='_compute_shipping_weight') + @api.onchange('carrier_id') + def onchange_carrier(self): + if self.carrier_id.delivery_type in ['fixed', 'base_on_rule']: + self.carrier_price = self.carrier_id.price + @api.depends('product_id', 'move_lines') def _cal_weight(self): for picking in self: diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index ecdda31d27d..88fe6423761 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -418,6 +418,13 @@ class HrExpenseSheet(models.Model): self._add_followers() return res + @api.multi + def unlink(self): + for expense in self: + if expense.state == "post": + raise UserError(_("You cannot delete a posted expense.")) + super(HrExpenseSheet, self).unlink() + @api.multi def set_to_paid(self): self.write({'state': 'done'}) diff --git a/addons/hr_holidays/views/hr_holidays_views.xml b/addons/hr_holidays/views/hr_holidays_views.xml index fc30ddde24c..3ae41455a7f 100644 --- a/addons/hr_holidays/views/hr_holidays_views.xml +++ b/addons/hr_holidays/views/hr_holidays_views.xml @@ -29,7 +29,7 @@ - + diff --git a/addons/hr_timesheet_sheet/views/hr_timesheet_sheet_views.xml b/addons/hr_timesheet_sheet/views/hr_timesheet_sheet_views.xml index ff0f856ebcf..059c4d071c4 100644 --- a/addons/hr_timesheet_sheet/views/hr_timesheet_sheet_views.xml +++ b/addons/hr_timesheet_sheet/views/hr_timesheet_sheet_views.xml @@ -66,7 +66,7 @@
diff --git a/addons/l10n_be_invoice_bba/models/account_invoice.py b/addons/l10n_be_invoice_bba/models/account_invoice.py index 68bad8964b3..e82f2e9a20b 100644 --- a/addons/l10n_be_invoice_bba/models/account_invoice.py +++ b/addons/l10n_be_invoice_bba/models/account_invoice.py @@ -48,7 +48,7 @@ class AccountInvoice(models.Model): if mod == int(bbacomm[-2:]): return True - @api.onchange('partner_id') + @api.onchange('partner_id', 'type') def _onchange_partner_id(self): result = super(AccountInvoice, self)._onchange_partner_id() reference = False diff --git a/addons/l10n_be_invoice_bba/views/account_invoice_view.xml b/addons/l10n_be_invoice_bba/views/account_invoice_view.xml index 3c4bdc0af8c..9ab348940a1 100644 --- a/addons/l10n_be_invoice_bba/views/account_invoice_view.xml +++ b/addons/l10n_be_invoice_bba/views/account_invoice_view.xml @@ -7,8 +7,7 @@ - + diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js index adf2d5714f6..fe9ad84ba39 100644 --- a/addons/mail/static/src/js/client_action.js +++ b/addons/mail/static/src/js/client_action.js @@ -662,6 +662,10 @@ var ChatAction = Widget.extend(ControlPanelMixin, { target: 'current' }); }, + destroy: function() { + this.$buttons.off().destroy(); + this._super.apply(this, arguments); + }, }); diff --git a/addons/mail/static/src/js/composer.js b/addons/mail/static/src/js/composer.js index 10c99ae6eb0..dd30dddf8f3 100644 --- a/addons/mail/static/src/js/composer.js +++ b/addons/mail/static/src/js/composer.js @@ -461,6 +461,11 @@ var BasicComposer = Widget.extend({ return this._super(); }, + destroy: function () { + $(window).off(this.fileupload_id); + return this._super.apply(this, arguments); + }, + toggle: function(state) { this.$el.toggle(state); }, diff --git a/addons/mass_mailing/static/src/less/themes/theme_default.less b/addons/mass_mailing/static/src/less/themes/theme_default.less index 8999132c2ae..2c01e9be498 100644 --- a/addons/mass_mailing/static/src/less/themes/theme_default.less +++ b/addons/mass_mailing/static/src/less/themes/theme_default.less @@ -141,6 +141,9 @@ display: inline-block; } } + td { + vertical-align: middle; + } } .o_mail_block_header_text_social { @@ -187,6 +190,18 @@ height: 1px; background-color: darken(@o_mm_def_color_alpha, 15%); } + table table { + td, th { + vertical-align: middle; + } + td { + padding: 10px 20px; + * { + margin: 0; + line-height: 1.3; + } + } + } } .o_mail_block_discount2 { @@ -202,6 +217,7 @@ .o_mail_table_styles { border-top: 2px solid @o_mm_def_color_alpha; padding-top: 20px; + padding-bottom: 20px; } .o_mail_footer_links { @@ -241,6 +257,9 @@ } } } + td { + vertical-align: middle; + } } .o_mail_display_coupon { diff --git a/addons/mass_mailing/views/snippets_themes.xml b/addons/mass_mailing/views/snippets_themes.xml index dad98262117..e92bbdcdd03 100644 --- a/addons/mass_mailing/views/snippets_themes.xml +++ b/addons/mass_mailing/views/snippets_themes.xml @@ -245,22 +245,22 @@ -

Basic features

+

Basic features

-

Basic management

+

Basic management

-

No customization

+

No customization

-

No support

+

No support

@@ -301,22 +301,22 @@ -

Advanced features

+

Advanced features

-

Total management

+

Total management

-

Fully customizable

+

Fully customizable

-

24/7 Support

+

24/7 Support

diff --git a/addons/payment/views/payment_views.xml b/addons/payment/views/payment_views.xml index b652d8d9a3d..edac67bdd4f 100644 --- a/addons/payment/views/payment_views.xml +++ b/addons/payment/views/payment_views.xml @@ -225,7 +225,7 @@ - +