[IMP] payment_forte: improve unit test and allow setting journal on the payment acquirer

This commit is contained in:
Cedric Collins
2022-10-14 16:29:06 -05:00
parent e52c947da6
commit 941310d26b
6 changed files with 79 additions and 103 deletions

View File

@@ -11,4 +11,5 @@
'data/payment_acquirer_data.xml', 'data/payment_acquirer_data.xml',
], ],
'installable': True, 'installable': True,
'license': 'LGPL-3',
} }

View File

@@ -6,25 +6,23 @@
<field name="name">Forte</field> <field name="name">Forte</field>
<field name="provider">forte</field> <field name="provider">forte</field>
<field name="company_id" ref="base.main_company"/> <field name="company_id" ref="base.main_company"/>
<field name="state">test</field> <field name="support_authorization">True</field>
<field name="forte_organization_id">1000</field> <field name="support_fees_computation">False</field>
<field name="forte_location_id">1001</field> <field name="support_refund"></field>
<field name="forte_access_id">dummy</field> <field name="support_tokenization">True</field>
<field name="forte_secure_key">dummy</field> <field name="allow_tokenization">True</field>
</record> </record>
<record id="payment_method_forte_echeck_inbound" model="account.payment.method"> <record id="payment_method_forte_echeck_inbound" model="account.payment.method">
<field name="name">Forte eCheck</field> <field name="name">Forte eCheck</field>
<field name="code">forte</field> <field name="code">forte</field>
<field name="payment_type">inbound</field> <field name="payment_type">inbound</field>
<field name="forte_type">echeck</field>
</record> </record>
<record id="payment_method_forte_echeck_outbound" model="account.payment.method"> <record id="payment_method_forte_echeck_outbound" model="account.payment.method">
<field name="name">Forte eCheck</field> <field name="name">Forte eCheck</field>
<field name="code">forte</field> <field name="code">forte</field>
<field name="payment_type">outbound</field> <field name="payment_type">outbound</field>
<field name="forte_type">echeck</field>
</record> </record>
</data> </data>

View File

@@ -3,11 +3,6 @@ from odoo import api, models, fields
class AccountPaymentMethod(models.Model): class AccountPaymentMethod(models.Model):
_inherit = 'account.payment.method' _inherit = 'account.payment.method'
forte_type = fields.Selection([
('echeck', 'eCheck'),
('creditcard', 'Credit Card'),
])
@api.model @api.model
def _get_payment_method_information(self): def _get_payment_method_information(self):
res = super()._get_payment_method_information() res = super()._get_payment_method_information()

View File

@@ -1,7 +1,11 @@
import logging
from odoo import api, fields, models from odoo import api, fields, models
from odoo.exceptions import ValidationError from odoo.exceptions import UserError
from .forte_request import ForteAPI from .forte_request import ForteAPI
from json import dumps
_logger = logging.getLogger(__name__)
def forte_get_api(acquirer): def forte_get_api(acquirer):
@@ -21,31 +25,11 @@ class PaymentAcquirerForte(models.Model):
forte_access_id = fields.Char(string='Access ID') forte_access_id = fields.Char(string='Access ID')
forte_secure_key = fields.Char(string='Secure Key') forte_secure_key = fields.Char(string='Secure Key')
def _get_feature_support(self): def _get_default_payment_method_id(self):
"""Get advanced feature support by provider.
Each provider should add its technical in the corresponding
key for the following features:
* fees: support payment fees computations
* authorize: support authorizing payment (separates
authorization and capture)
* tokenize: support saving payment data in a payment.tokenize
object
"""
res = super(PaymentAcquirerForte, self)._get_feature_support()
res['authorize'].append('authorize')
res['tokenize'].append('authorize')
return res
def forte_test_credentials(self):
self.ensure_one() self.ensure_one()
api = forte_get_api(self) if self.provider != 'forte':
resp = api.test_authenticate() return super()._get_default_payment_method_id()
if not resp.ok: return self.env.ref('payment_forte.payment_method_forte_echeck_inbound').id
result = resp.json()
if result and result.get('response'):
raise ValidationError('Error: ' + dumps(result.get('response')))
return True
class TxForte(models.Model): class TxForte(models.Model):
@@ -75,24 +59,21 @@ class TxForte(models.Model):
account_holder = self.token_id.forte_account_holder account_holder = self.token_id.forte_account_holder
method = self.payment_id.payment_method_id method = self.payment_id.payment_method_id
# if not self.env.context.get('payment_type'):
if not method or not method.payment_type: if not method or not method.payment_type:
_logger.warning('Trying to do a payment with Forte and no contextual payment_type will result in an inbound transaction.') _logger.warning('Trying to do a payment with Forte and no contextual payment_type will result in an inbound transaction.')
# if self.env.context.get('payment_type') == 'inbound':
if method.forte_type == 'echeck':
if method.payment_type == 'outbound': if method.payment_type == 'outbound':
resp = api.echeck_credit(location, amount, account_type, routing_number, account_number, account_holder) resp = api.echeck_credit(location, amount, account_type, routing_number, account_number, account_holder)
else: else:
resp = api.echeck_sale(location, amount, account_type, routing_number, account_number, account_holder) resp = api.echeck_sale(location, amount, account_type, routing_number, account_number, account_holder)
# elif method.forte_type == 'creditcard':
if resp.ok and resp.json()['response']['response_desc'] == 'APPROVED': if resp.ok and resp.json()['response']['response_desc'] == 'APPROVED':
ref = resp.json()['response']['authorization_code'] ref = resp.json()['response']['authorization_code']
return self.write({'state': 'done', 'acquirer_reference': ref}) return self.write({'state': 'done', 'acquirer_reference': ref})
else: else:
result = resp.json() result = resp.json() and resp.json().get('response')
if result and result.get('response'): if result:
raise ValidationError('Error: ' + dumps(result.get('response'))) raise UserError('Error: %s - %s' % (result.get('response_code'), result.get('response_desc')))
class PaymentToken(models.Model): class PaymentToken(models.Model):

View File

@@ -1,35 +1,43 @@
from odoo.addons.payment.tests.common import PaymentCommon from json import dumps
from unittest import SkipTest
import logging
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from odoo.tests import tagged from odoo.tests import tagged
from odoo.addons.payment.tests.common import PaymentCommon
from ..models.payment import forte_get_api
_logger = logging.getLogger(__name__)
class ForteCommon(PaymentCommon): class ForteCommon(PaymentCommon):
@classmethod @classmethod
def setUpClass(cls, chart_template_ref=None): def setUpClass(cls, chart_template_ref=None):
super().setUpClass(chart_template_ref=chart_template_ref) super().setUpClass(chart_template_ref=chart_template_ref)
cls.currency_usd = cls._prepare_currency('USD')
cls.forte = cls._prepare_acquirer('forte', update_values={ cls.forte = cls._prepare_acquirer('forte', update_values={
'fees_active': False, # Only activate fees in dedicated tests 'fees_active': False, # Only activate fees in dedicated tests
'forte_organization_id': '366035',
'forte_location_id': '223008',
'forte_access_id': 'a7b45fe9ff8d1d5406ae6f98791958da',
'forte_secure_key': '20ee1d1f5a4afbf9db03af2c81f067c5',
}) })
if not cls.forte.forte_secure_key:
skip_message = 'Credentials have not been configured for Forte, skipping tests...'
_logger.warning(skip_message)
raise SkipTest(skip_message)
# override defaults for helpers # override defaults for helpers
cls.acquirer = cls.forte cls.acquirer = cls.forte
cls.currency = cls.currency_usd cls.inbound_payment_method_line = cls.acquirer.journal_id.inbound_payment_method_line_ids.filtered(lambda l: l.code == 'forte')
cls.forte = cls.acquirer
cls.method = cls.env.ref('payment_forte.payment_method_forte_echeck_inbound') def forte_test_credentials(self):
cls.journal = cls.env['account.journal'].search([], limit=1)[0] api = forte_get_api(self.acquirer)
cls.journal.write({ resp = api.test_authenticate()
'inbound_payment_method_line_ids': [(0, 0, { if not resp.ok:
'name': 'Electronic', result = resp.json()
'payment_method_id': cls.method.id, if result and result.get('response'):
})], raise ValidationError('Error: ' + dumps(result.get('response')))
}) return True
cls.method_line = cls.journal.inbound_payment_method_line_ids.filtered(lambda l: l.payment_method_id == cls.method)
cls.buyer = cls.env['res.partner'].search([('customer_rank', '>=', 1)], limit=1)[0]
@tagged('post_install', '-at_install') @tagged('post_install', '-at_install')
@@ -37,38 +45,31 @@ class ForteACH(ForteCommon):
def test_10_forte_api(self): def test_10_forte_api(self):
self.assertEqual(self.forte.state, 'test', 'Must test with test environment.') self.assertEqual(self.forte.state, 'test', 'Must test with test environment.')
response = self.forte.forte_test_credentials() self.assertTrue(self.forte_test_credentials())
# Create/Save a Payment Token. # Create/Save a Payment Token.
# Change Token numbers to real values if you want to try to get an approval. # Change Token numbers to real values if you want to try to get an approval.
token = self.env['payment.token'].create({ token = self.create_token(
'name': 'Test Token 1234', forte_account_type='Checking',
'partner_id': self.buyer.id, forte_routing_number='021000021',
'acquirer_id': self.forte.id, forte_account_number='000111222',
'acquirer_ref': 'Test Token', forte_account_holder=self.partner.name,
'forte_account_type': 'Checking', )
'forte_routing_number': '021000021',
'forte_account_number': '000111222',
'forte_account_holder': self.buyer.name,
})
# Create Payment # Create Payment
try:
payment = self.env['account.payment'].create({ payment = self.env['account.payment'].create({
'payment_type': 'inbound', 'payment_type': 'inbound',
'journal_id': self.journal.id, 'partner_type': 'customer',
'partner_id': self.buyer.id, 'amount': 22.0,
# 'date': '2019-01-01',
'currency_id': self.currency.id,
'partner_id': self.partner.id,
'journal_id': self.acquirer.journal_id.id,
'payment_method_line_id': self.inbound_payment_method_line.id,
'payment_token_id': token.id, 'payment_token_id': token.id,
'payment_method_id': self.method.id,
'payment_method_line_id': self.method_line.id,
'amount': 22.00,
}) })
payment.action_post() payment.action_post()
self.assertTrue(payment.payment_transaction_id) self.assertTrue(payment.payment_transaction_id)
self.assertEqual(payment.payment_transaction_id.amount, 22.00) self.assertEqual(payment.payment_transaction_id.amount, 22.00)
self.assertTrue(payment.payment_transaction_id.acquirer_reference) self.assertTrue(payment.payment_transaction_id.acquirer_reference)
except ValidationError as e:
# U02 account not authorized.
if e.name.find('U02') < 0:
raise e

View File

@@ -7,10 +7,10 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr='//group[@name="acquirer"]' position='after'> <xpath expr='//group[@name="acquirer"]' position='after'>
<group attrs="{'invisible': [('provider', '!=', 'forte')]}"> <group attrs="{'invisible': [('provider', '!=', 'forte')]}">
<field name="forte_organization_id"/> <field name="forte_organization_id" attrs="{'required': [('provider', '=', 'forte'),('state', '!=', 'disabled')]}"/>
<field name="forte_location_id"/> <field name="forte_location_id" attrs="{'required': [('provider', '=', 'forte'),('state', '!=', 'disabled')]}"/>
<field name="forte_access_id"/> <field name="forte_access_id" attrs="{'required': [('provider', '=', 'forte'),('state', '!=', 'disabled')]}"/>
<field name="forte_secure_key" password="True"/> <field name="forte_secure_key" password="True" attrs="{'required': [('provider', '=', 'forte'),('state', '!=', 'disabled')]}"/>
</group> </group>
</xpath> </xpath>
</field> </field>