diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index a1fa22a824c..40b1cd90ff5 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -394,7 +394,8 @@ class ResPartner(models.Model): required=True) property_account_position_id = fields.Many2one('account.fiscal.position', company_dependent=True, string="Fiscal Position", - help="The fiscal position will determine taxes and accounts used for the partner.", oldname="property_account_position") + help="The fiscal position will determine taxes and accounts used for the partner.", oldname="property_account_position", + domain="[('company_id', 'in', [company_id, False])]") property_payment_term_id = fields.Many2one('account.payment.term', company_dependent=True, string='Customer Payment Terms', help="This payment term will be used instead of the default one for sales orders and customer invoices", oldname="property_payment_term") @@ -441,3 +442,8 @@ class ResPartner(models.Model): action['domain'] = literal_eval(action['domain']) action['domain'].append(('partner_id', 'child_of', self.id)) return action + + @api.onchange('company_id') + def _onchange_company_id(self): + if self.company_id: + return {'domain': {'property_account_position_id': [('company_id', 'in', [self.company_id.id, False])]}} diff --git a/addons/account/views/partner_view.xml b/addons/account/views/partner_view.xml index b4157614966..bc325f129bd 100644 --- a/addons/account/views/partner_view.xml +++ b/addons/account/views/partner_view.xml @@ -108,6 +108,7 @@ + diff --git a/addons/account/wizard/wizard_tax_adjustments.py b/addons/account/wizard/wizard_tax_adjustments.py index d498bb62afb..2f188b1abab 100644 --- a/addons/account/wizard/wizard_tax_adjustments.py +++ b/addons/account/wizard/wizard_tax_adjustments.py @@ -18,24 +18,26 @@ class TaxAdjustments(models.TransientModel): debit_account_id = fields.Many2one('account.account', string='Debit account', required=True, domain=[('deprecated', '=', False)]) credit_account_id = fields.Many2one('account.account', string='Credit account', required=True, domain=[('deprecated', '=', False)]) amount = fields.Monetary(currency_field='company_currency_id', required=True) + adjustment_type = fields.Selection([('debit', 'Adjustment in favor of the Estate'), ('credit', 'Adjustment in your favor')], string="Adjustment Type", store=False, required=True) company_currency_id = fields.Many2one('res.currency', readonly=True, default=lambda self: self.env.user.company_id.currency_id) tax_id = fields.Many2one('account.tax', string='Adjustment Tax', ondelete='restrict', domain=[('type_tax_use', '=', 'adjustment')], required=True) @api.multi def _create_move(self): + adjustment_type = self.env.context.get('adjustment_type', (self.amount > 0.0 and 'debit' or 'credit')) debit_vals = { 'name': self.reason, - 'debit': self.amount, + 'debit': abs(self.amount), 'credit': 0.0, 'account_id': self.debit_account_id.id, - 'tax_line_id': self.tax_id.id, + 'tax_line_id': adjustment_type == 'debit' and self.tax_id.id or False, } credit_vals = { 'name': self.reason, 'debit': 0.0, - 'credit': self.amount, + 'credit': abs(self.amount), 'account_id': self.credit_account_id.id, - 'tax_line_id': self.tax_id.id, + 'tax_line_id': adjustment_type == 'credit' and self.tax_id.id or False, } vals = { 'journal_id': self.journal_id.id, @@ -48,6 +50,13 @@ class TaxAdjustments(models.TransientModel): return move.id @api.multi + def create_move_debit(self): + return self.with_context(adjustment_type='debit').create_move() + + @api.multi + def create_move_credit(self): + return self.with_context(adjustment_type='credit').create_move() + def create_move(self): #create the adjustment move move_id = self._create_move() diff --git a/addons/account/wizard/wizard_tax_adjustments_view.xml b/addons/account/wizard/wizard_tax_adjustments_view.xml index 3d4ed0a9e9a..dba164388b9 100644 --- a/addons/account/wizard/wizard_tax_adjustments_view.xml +++ b/addons/account/wizard/wizard_tax_adjustments_view.xml @@ -12,6 +12,7 @@ + @@ -26,8 +27,14 @@
-
diff --git a/addons/account_asset/models/account_asset.py b/addons/account_asset/models/account_asset.py index 3bb25d93e73..3b29938007c 100644 --- a/addons/account_asset/models/account_asset.py +++ b/addons/account_asset/models/account_asset.py @@ -419,7 +419,7 @@ class AccountAssetAsset(models.Model): @api.model def create(self, vals): asset = super(AccountAssetAsset, self.with_context(mail_create_nolog=True)).create(vals) - asset.compute_depreciation_board() + asset.sudo().compute_depreciation_board() return asset @api.multi diff --git a/addons/account_payment/controllers/payment.py b/addons/account_payment/controllers/payment.py index e2a5e6dbea9..2916a512b51 100644 --- a/addons/account_payment/controllers/payment.py +++ b/addons/account_payment/controllers/payment.py @@ -8,7 +8,7 @@ from odoo.http import request, route class PaymentPortal(http.Controller): - @route('/invoice/pay//form_tx', type='json', auth="user", website=True) + @route('/invoice/pay//form_tx', type='json', auth="public", website=True) def invoice_pay_form(self, acquirer_id, invoice_id, save_token=False, access_token=None, **kwargs): """ Json method that creates a payment.transaction, used to create a transaction when the user clicks on 'pay now' button on the payment @@ -17,13 +17,11 @@ class PaymentPortal(http.Controller): :return html: form containing all values related to the acquirer to redirect customers to the acquirer website """ success_url = kwargs.get('success_url', '/my') + callback_method = kwargs.get('callback_method', '') + invoice_sudo = request.env['account.invoice'].sudo().browse(invoice_id) if not invoice_sudo: return False - # Check if the current user has access to this invoice - commercial_partner_id = request.env.user.partner_id.commercial_partner_id.id - if request.env['account.invoice'].sudo().search_count([('id', '=', invoice_id), ('message_partner_ids', 'child_of', commercial_partner_id)]) == 0: - return False try: acquirer = request.env['payment.acquirer'].browse(int(acquirer_id)) @@ -35,7 +33,12 @@ class PaymentPortal(http.Controller): invoice_sudo, acquirer, payment_token=token, - tx_type='form_save' if save_token else 'form') + tx_type='form_save' if save_token else 'form', + add_tx_values={ + 'callback_model_id': request.env['ir.model'].sudo().search([('model', '=', invoice_sudo._name)], limit=1).id, + 'callback_res_id': invoice_sudo.id, + 'callback_method': callback_method, + }) # set the transaction id into the session request.session['portal_invoice_%s_transaction_id' % invoice_sudo.id] = tx.id @@ -50,11 +53,12 @@ class PaymentPortal(http.Controller): } ) - @http.route('/invoice/pay//s2s_token_tx', type='http', auth='user', website=True) + @http.route('/invoice/pay//s2s_token_tx', type='http', auth='public', website=True) def invoice_pay_token(self, invoice_id, pm_id=None, **kwargs): """ Use a token to perform a s2s transaction """ error_url = kwargs.get('error_url', '/my') success_url = kwargs.get('success_url', '/my') + callback_method = kwargs.get('callback_method', '') access_token = kwargs.get('access_token') params = {} if access_token: @@ -65,11 +69,6 @@ class PaymentPortal(http.Controller): params['error'] = 'pay_invoice_invalid_doc' return request.redirect(_build_url_w_params(error_url, params)) - # Check if the current user has access to this invoice - commercial_partner_id = request.env.user.partner_id.commercial_partner_id.id - if request.env['account.invoice'].sudo().search_count([('id', '=', invoice_id), ('message_partner_ids', 'child_of', commercial_partner_id)]) == 0: - return False - try: token = request.env['payment.token'].sudo().browse(int(pm_id)) except (ValueError, TypeError): @@ -83,7 +82,12 @@ class PaymentPortal(http.Controller): invoice_sudo, token.acquirer_id, payment_token=token, - tx_type='server2server') + tx_type='server2server', + add_tx_values={ + 'callback_model_id': request.env['ir.model'].sudo().search([('model', '=', invoice_sudo._name)], limit=1).id, + 'callback_res_id': invoice_sudo.id, + 'callback_method': callback_method, + }) # set the transaction id into the session request.session['portal_invoice_%s_transaction_id' % invoice_sudo.id] = tx.id diff --git a/addons/base_import/models/base_import.py b/addons/base_import/models/base_import.py index ff59aff918a..70ac02fefef 100644 --- a/addons/base_import/models/base_import.py +++ b/addons/base_import/models/base_import.py @@ -497,7 +497,6 @@ class Import(models.TransientModel): 'headers_type': header_types or False, 'preview': preview, 'options': options, - 'advanced_mode': any([len(models.fix_import_export_id_paths(col)) > 1 for col in headers or []]), 'debug': self.user_has_groups('base.group_no_one'), } except Exception as error: diff --git a/addons/base_import/static/src/js/import_action.js b/addons/base_import/static/src/js/import_action.js index 11b81a6373a..a2dc616ee2c 100644 --- a/addons/base_import/static/src/js/import_action.js +++ b/addons/base_import/static/src/js/import_action.js @@ -307,7 +307,6 @@ var DataImport = Widget.extend(ControlPanelMixin, { this.$buttons.filter('.o_import_button').add(this.$('.oe_import_file_reload')) .prop('disabled', false); this.$el.addClass('oe_import_preview'); - this.$('input.oe_import_advanced_mode').prop('checked', result.advanced_mode); this.$('.oe_import_grid').html(QWeb.render('ImportView.preview', result)); if (result.headers.length === 1) { diff --git a/addons/base_import/static/src/xml/base_import.xml b/addons/base_import/static/src/xml/base_import.xml index 4130b1d0347..f882bf7f557 100644 --- a/addons/base_import/static/src/xml/base_import.xml +++ b/addons/base_import/static/src/xml/base_import.xml @@ -81,7 +81,7 @@ id="oe_import_has_header" checked="checked"/> -

If the file contains diff --git a/addons/base_import/tests/test_base_import.py b/addons/base_import/tests/test_base_import.py index 55f3300da91..df2d18a3feb 100644 --- a/addons/base_import/tests/test_base_import.py +++ b/addons/base_import/tests/test_base_import.py @@ -258,7 +258,7 @@ class TestPreview(TransactionCase): ['qux', '5', '6'], ]) # Ensure we only have the response fields we expect - self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug']) + self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'debug']) @unittest.skipUnless(can_import('xlrd'), "XLRD module not available") def test_xls_success(self): @@ -288,7 +288,7 @@ class TestPreview(TransactionCase): ['qux', '5', '6'], ]) # Ensure we only have the response fields we expect - self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug']) + self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'debug']) @unittest.skipUnless(can_import('xlrd.xlsx'), "XLRD/XLSX not available") def test_xlsx_success(self): @@ -318,7 +318,7 @@ class TestPreview(TransactionCase): ['qux', '5', '6'], ]) # Ensure we only have the response fields we expect - self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options','advanced_mode', 'debug']) + self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'debug']) @unittest.skipUnless(can_import('odf'), "ODFPY not available") def test_ods_success(self): @@ -348,7 +348,7 @@ class TestPreview(TransactionCase): ['aux', '5', '6'], ]) # Ensure we only have the response fields we expect - self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'advanced_mode', 'debug']) + self.assertItemsEqual(list(result), ['matches', 'headers', 'fields', 'preview', 'headers_type', 'options', 'debug']) class test_convert_import_data(TransactionCase): diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 2e56eb39685..5793908c02b 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -141,6 +141,31 @@ FormRenderer.include({ this._super.apply(this, arguments); this.noContentHelp = params.noContentHelp; this.actionsDescr = {}; + this._boardSubcontrollers = []; // for board: controllers of subviews + }, + /** + * Call `on_attach_callback` for each subview + * + * @override + */ + on_attach_callback: function () { + _.each(this._boardSubcontrollers, function (controller) { + if ('on_attach_callback' in controller) { + controller.on_attach_callback(); + } + }); + }, + /** + * Call `on_detach_callback` for each subview + * + * @override + */ + on_detach_callback: function () { + _.each(this._boardSubcontrollers, function (controller) { + if ('on_detach_callback' in controller) { + controller.on_detach_callback(); + } + }); }, //-------------------------------------------------------------------------- @@ -243,6 +268,7 @@ FormRenderer.include({ hasSelectors: false, }); return view.getController(self).then(function (controller) { + self._boardSubcontrollers.push(controller); return controller.appendTo(params.$node); }); }); diff --git a/addons/board/static/tests/dashboard_tests.js b/addons/board/static/tests/dashboard_tests.js index 0a1264dc589..6eaeae518b1 100644 --- a/addons/board/static/tests/dashboard_tests.js +++ b/addons/board/static/tests/dashboard_tests.js @@ -3,6 +3,7 @@ odoo.define('board.dashboard_tests', function (require) { var testUtils = require('web.test_utils'); var FormView = require('web.FormView'); +var ListRenderer = require('web.ListRenderer'); var createView = testUtils.createView; @@ -454,5 +455,48 @@ QUnit.test('clicking on a kanban\'s button should trigger the action', function form.destroy(); }); +QUnit.test('subviews are aware of attach in or detach from the DOM', function (assert) { + assert.expect(2); + + // patch list renderer `on_attach_callback` for the test only + testUtils.patch(ListRenderer, { + on_attach_callback: function () { + assert.step('subview on_attach_callback'); + } + }); + + var form = createView({ + View: FormView, + model: 'board', + data: this.data, + arch: '

' + + '' + + '' + + '' + + '' + + '' + + '
', + mockRPC: function (route) { + if (route === '/web/action/load') { + return $.when({ + res_model: 'partner', + views: [[4, 'list']], + }); + } + return this._super.apply(this, arguments); + }, + archs: { + 'partner,4,list': + '', + }, + }); + + assert.verifySteps(['subview on_attach_callback']); + + // restore on_attach_callback of ListRenderer + testUtils.unpatch(ListRenderer); + + form.destroy(); +}); }); diff --git a/addons/google_calendar/views/res_config_settings_views.xml b/addons/google_calendar/views/res_config_settings_views.xml index a7874c34c69..c5c7d19a1b7 100644 --- a/addons/google_calendar/views/res_config_settings_views.xml +++ b/addons/google_calendar/views/res_config_settings_views.xml @@ -13,12 +13,13 @@