Skip to content

Commit 64230ba

Browse files
committed
[FIX] payment: enable manual capture for payment method brands
Versions -------- - saas-18.2+ Steps ----- 1. Set up Adyen as a payment provider; 2. enable manual capture; 3. set up a sales order; 4. pay for sales order with Adyen using VISA; 5. do a partial manual capture. Issue ----- Payment method doesn't support partial capture. Additionally, you're not allowed to enable the Hipercard & Elo payment method brands for the Card payment method. Cause ----- Commit 25feb5b added the `support_manual_capture` field to `payment.method`. This field is only set to a non-default value for primary payment methods, e.g. Card. For branded payment method, e.g. VISA, it defaults to 'none'. Solution -------- Add a `_get_primary_method` to `payment.method` and `payment.transaction`, retrieving the relevant primary payment method to check support on. On master: add a `primary_payment_method_id` computed field on `payment.transaction`. opw-4817961 closes odoo#217743 X-original-commit: c4823e8 Signed-off-by: Antoine Vandevenne (anv) <[email protected]> Signed-off-by: Levi Siuzdak <[email protected]>
1 parent 9cc1266 commit 64230ba

File tree

6 files changed

+90
-5
lines changed

6 files changed

+90
-5
lines changed

addons/payment/models/payment_method.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,10 @@ def _onchange_provider_ids_warn_before_attaching_payment_method(self):
180180
@api.constrains('active', 'support_manual_capture')
181181
def _check_manual_capture_supported_by_providers(self):
182182
incompatible_pms = self.filtered(
183-
lambda method: method.active and method.support_manual_capture == 'none' and any(
184-
provider.capture_manually for provider in method.provider_ids
185-
)
183+
lambda pm:
184+
pm.active
185+
and (pm.primary_payment_method_id or pm).support_manual_capture == 'none'
186+
and any(provider.capture_manually for provider in pm.provider_ids),
186187
)
187188
if incompatible_pms:
188189
raise ValidationError(_(

addons/payment/models/payment_transaction.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ def _lang_get(self):
4343
payment_method_code = fields.Char(
4444
string="Payment Method Code", related='payment_method_id.code'
4545
)
46+
primary_payment_method_id = fields.Many2one(
47+
string="Primary Payment Method",
48+
comodel_name='payment.method',
49+
compute='_compute_primary_payment_method_id',
50+
)
4651
reference = fields.Char(
4752
string="Reference", help="The internal reference of the transaction", readonly=True,
4853
required=True) # Already has an index from the UNIQUE SQL constraint.
@@ -128,6 +133,10 @@ def _lang_get(self):
128133

129134
#=== COMPUTE METHODS ===#
130135

136+
def _compute_primary_payment_method_id(self):
137+
for pm, txs in self.grouped('payment_method_id').items():
138+
txs.primary_payment_method_id = pm.primary_payment_method_id or pm
139+
131140
def _compute_refunds_count(self):
132141
rg_data = self.env['payment.transaction']._read_group(
133142
domain=[('source_transaction_id', 'in', self.ids), ('operation', '=', 'refund')],

addons/payment/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from . import http_common
55
from . import test_flows
66
from . import test_multicompany_flows
7+
from . import test_payment_capture_wizard
78
from . import test_payment_method
89
from . import test_payment_provider
910
from . import test_payment_token
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Part of Odoo. See LICENSE file for full copyright and licensing details.
2+
3+
from odoo.tests import tagged
4+
5+
from odoo.addons.payment.tests.common import PaymentCommon
6+
7+
8+
@tagged('post_install', '-at_install')
9+
class TestPaymentCaptureWizard(PaymentCommon):
10+
11+
def test_partial_capture_wizard(self):
12+
self.provider.update({
13+
'capture_manually': True,
14+
'support_manual_capture': 'partial',
15+
})
16+
source_tx = self._create_transaction('direct', state='authorized')
17+
18+
wizard = self.env['payment.capture.wizard'].create({
19+
'transaction_ids': source_tx.ids,
20+
})
21+
wizard.amount_to_capture = 511.11
22+
wizard.action_capture()
23+
24+
child_tx_1 = source_tx.child_transaction_ids
25+
self.assertEqual(child_tx_1.state, 'draft')
26+
child_tx_1._set_done()
27+
28+
self.env['payment.capture.wizard'].create({
29+
'transaction_ids': source_tx.ids,
30+
}).action_capture()
31+
32+
child_tx_2 = (source_tx.child_transaction_ids - child_tx_1).ensure_one()
33+
child_tx_2._set_done()
34+
self.assertAlmostEqual(
35+
sum(source_tx.child_transaction_ids.mapped('amount')),
36+
source_tx.amount,
37+
)
38+
self.assertEqual(source_tx.state, 'done')
39+
40+
def test_support_partial_capture_computation_with_brands(self):
41+
self.provider.update({
42+
'capture_manually': True,
43+
'support_manual_capture': 'partial',
44+
})
45+
dummy_brand = self.env['payment.method'].create({
46+
'name': "Dummy Brand",
47+
'code': 'dumbrand',
48+
'primary_payment_method_id': self.payment_method.id,
49+
'provider_ids': self.provider.ids,
50+
})
51+
source_tx = self._create_transaction(
52+
'direct', state='authorized', payment_method_id=dummy_brand.id,
53+
)
54+
wizard = self.env['payment.capture.wizard'].create({
55+
'transaction_ids': source_tx.ids,
56+
})
57+
self.assertTrue(wizard.support_partial_capture)

addons/payment/tests/test_payment_method.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Part of Odoo. See LICENSE file for full copyright and licensing details.
22

3-
from odoo.exceptions import UserError
3+
from odoo.exceptions import UserError, ValidationError
44
from odoo.fields import Command
55
from odoo.tests import tagged
66

@@ -26,6 +26,23 @@ def test_payment_method_requires_provider_to_be_activated(self):
2626
with self.assertRaises(UserError):
2727
self.payment_methods.active = True
2828

29+
def test_brand_compatible_with_manual_capture(self):
30+
""" Test that a "brand" can be enabled for providers which support manual capture. """
31+
self.provider.update({
32+
'capture_manually': True,
33+
'support_manual_capture': 'partial',
34+
})
35+
self.payment_method.support_manual_capture = 'partial'
36+
brand_payment_method = self.env['payment.method'].create({
37+
'name': "Dummy Brand",
38+
'code': 'dumbrand',
39+
'primary_payment_method_id': self.payment_method.id,
40+
'active': False,
41+
'provider_ids': self.provider.ids,
42+
})
43+
self._assert_does_not_raise(ValidationError, brand_payment_method.action_unarchive)
44+
self.assertTrue(brand_payment_method.active)
45+
2946
def test_payment_method_compatible_when_provider_is_enabled(self):
3047
""" Test that a payment method is available when it is supported by an enabled provider. """
3148
compatible_payment_methods = self.env['payment.method']._get_compatible_payment_methods(

addons/payment/wizards/payment_capture_wizard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def _compute_support_partial_capture(self):
8888
for wizard in self:
8989
wizard.support_partial_capture = all(
9090
tx.provider_id.support_manual_capture == 'partial'
91-
and tx.payment_method_id.support_manual_capture == 'partial'
91+
and tx.primary_payment_method_id.support_manual_capture == 'partial'
9292
for tx in wizard.transaction_ids
9393
)
9494

0 commit comments

Comments
 (0)