diff --git a/addons/payment/controllers/portal.py b/addons/payment/controllers/portal.py index d92ddae6beb..cb686e4707a 100644 --- a/addons/payment/controllers/portal.py +++ b/addons/payment/controllers/portal.py @@ -376,6 +376,22 @@ class PaymentPortal(portal.CustomerPortal): # Display the portal homepage to the user return request.redirect('/my/home') + @http.route('/payment/archive_token', type='json', auth='user') + def archive_token(self, token_id): + """ Check that a user has write access on a token and archive the token if so. + + :param int token_id: The token to archive, as a `payment.token` id + :return: None + """ + partner_sudo = request.env.user.partner_id + token_sudo = request.env['payment.token'].sudo().search([ + ('id', '=', token_id), + # Check that the user owns the token before letting them archive anything + ('partner_id', 'in', [partner_sudo.id, partner_sudo.commercial_partner_id.id]) + ]) + if token_sudo: + token_sudo.active = False + @staticmethod def cast_as_int(str_value): """ Cast a string as an `int` and return it. diff --git a/addons/payment/static/src/js/manage_form.js b/addons/payment/static/src/js/manage_form.js index aed87ff4550..a8c153816f7 100644 --- a/addons/payment/static/src/js/manage_form.js +++ b/addons/payment/static/src/js/manage_form.js @@ -80,19 +80,18 @@ odoo.define('payment.manage_form', require => { _deleteToken: function (tokenId) { const execute = () => { this._rpc({ - model: 'payment.token', - method: 'write', - args: [[tokenId], {active: false}], - }).then(result => { - if (result === true) { // Token successfully deleted, remove it from the view - const $tokenCard = this.$( - `input[name="o_payment_radio"][data-payment-option-id="${tokenId}"]` + - `[data-payment-option-type="token"]` - ).closest('div[name="o_payment_option_card"]'); - $tokenCard.siblings(`#o_payment_token_inline_form_${tokenId}`).remove(); - $tokenCard.remove(); - this._disableButton(false); - } + route: '/payment/archive_token', + params: { + 'token_id': tokenId, + }, + }).then(() => { + const $tokenCard = this.$( + `input[name="o_payment_radio"][data-payment-option-id="${tokenId}"]` + + `[data-payment-option-type="token"]` + ).closest('div[name="o_payment_option_card"]'); + $tokenCard.siblings(`#o_payment_token_inline_form_${tokenId}`).remove(); + $tokenCard.remove(); + this._disableButton(false); }).guardedCatch(error => { error.event.preventDefault(); this._displayError( diff --git a/addons/payment/tests/test_multicompany_flows.py b/addons/payment/tests/test_multicompany_flows.py index d887e903002..2bf214716c5 100644 --- a/addons/payment/tests/test_multicompany_flows.py +++ b/addons/payment/tests/test_multicompany_flows.py @@ -86,3 +86,21 @@ class TestMultiCompanyFlows(PaymentMultiCompanyCommon, PaymentHttpCommon): self.assertEqual(manage_context['acquirer_ids'], self.acquirer.ids) self.assertIn(token.id, manage_context['token_ids']) self.assertIn(token_company_b.id, manage_context['token_ids']) + + def test_archive_token_logged_in_another_company(self): + """User archives his token from another company.""" + # get user's token from company A + token = self.create_token(partner_id=self.portal_partner.id) + + # assign user to another company + company_b = self.env['res.company'].create({'name': 'Company B'}) + self.portal_user.write({'company_ids': [company_b.id], 'company_id': company_b.id}) + + # Log in as portal user + self.authenticate(self.portal_user.login, self.portal_user.login) + + # Archive token in company A + url = self._build_url('/payment/archive_token') + self._make_json_request(url, {'token_id': token.id}) + + self.assertFalse(token.active)