[IMP] payment_acquirer_paypal: added fees computation on paypal acquirer.

paypal acquirer model: added paypal_fee_active and paypal_fee_* fields: fix and variable
fees, for domestic and international transactions.

updated views accordingly + cleaned paypal form view.

updated tests

updated form, adding handling input in the paypal button that is the input controlling
the fees in the tx process.

When creating a paypal tx, it dynamically computes fees.

bzr revid: tde@openerp.com-20131128161707-ignhrlphts47wk72
This commit is contained in:
Thibault Delavallée 2013-11-28 17:17:07 +01:00
parent 27104e4888
commit ad2bdf6d94
5 changed files with 143 additions and 35 deletions

View File

@ -12,32 +12,41 @@
<field name="model">payment.acquirer</field>
<field name="arch" type="xml">
<form string="Payment Acquirer" version="7.0">
<group name="acquirer_base">
<field name="name"/>
<field name="portal_published"/>
<field name="env"/>
<field name="message"/>
<label for="view_template_id"/>
<div>
<field name="view_template_id" nolabel="1"/>
<sheet>
<group name="acquirer_base">
<group>
<field name="name"/>
<field name="company_id"/>
</group>
<group>
<field name="portal_published"/>
<field name="env"/>
</group>
</group>
<group name="acquirer_display">
<field name="message"/>
<label for="view_template_id"/>
<div>
This is an HTML form template to submit a payment through this acquirer.
The template will be rendered with qWeb, so it may use qWeb expressions.
The qWeb evaluation context provides:
<ul>
<li>acquirer: payment.acquirer browse record</li>
<li>user: current user browse record</li>
<li>reference: the transaction reference number</li>
<li>currency: the transaction currency browse record</li>
<li>amount: the transaction amount, a float</li>
<li>partner: the buyer partner browse record, not necessarily set</li>
<li>partner_values: specific values about the buyer, for example coming from a shipping form</li>
<li>tx_values: specific transaction values</li>
<li>context: the current context dictionary</li>
</ul>
<field name="view_template_id" nolabel="1"/>
<div>
This is an HTML form template to submit a payment through this acquirer.
The template will be rendered with qWeb, so it may use qWeb expressions.
The qWeb evaluation context provides:
<ul>
<li>acquirer: payment.acquirer browse record</li>
<li>user: current user browse record</li>
<li>reference: the transaction reference number</li>
<li>currency: the transaction currency browse record</li>
<li>amount: the transaction amount, a float</li>
<li>partner: the buyer partner browse record, not necessarily set</li>
<li>partner_values: specific values about the buyer, for example coming from a shipping form</li>
<li>tx_values: specific transaction values</li>
<li>context: the current context dictionary</li>
</ul>
</div>
</div>
</div>
</group>
</group>
</sheet>
</form>
</field>
</record>
@ -84,6 +93,7 @@
<group>
<field name="reference"/>
<field name="amount"/>
<field name="fees"/>
<field name="currency_id"/>
<field name="partner_id"/>
<field name="partner_name"/>

View File

@ -27,6 +27,13 @@ class AcquirerPaypal(osv.Model):
'paypal_username': fields.char('Username', required_if_provider='paypal'),
'paypal_tx_url': fields.char('Transaction URL', required_if_provider='paypal'),
'paypal_use_ipn': fields.boolean('Use IPN'),
# Fees
'paypal_fee_active': fields.boolean('Compute fees'),
'paypal_fee_dom_fixed': fields.float('Fixed domestic fees'),
'paypal_fee_dom_var': fields.float('Variable domestic fees (in percents)'),
'paypal_fee_int_fixed': fields.float('Fixed international fees'),
'paypal_fee_int_var': fields.float('Variable international fees (in percents)'),
# Server 2 server
'paypal_api_enabled': fields.boolean('Use Rest API'),
'paypal_api_username': fields.char('Rest API Username'),
'paypal_api_password': fields.char('Rest API Password'),
@ -35,11 +42,33 @@ class AcquirerPaypal(osv.Model):
}
_defaults = {
'paypal_use_ipn': True,
'paypal_api_enabled': False,
'paypal_tx_url': 'https://www.sandbox.paypal.com/cgi-bin/webscr',
'paypal_use_ipn': True,
'paypal_fee_active': False,
'paypal_fee_dom_fixed': 0.35,
'paypal_fee_dom_var': 3.4,
'paypal_fee_int_fixed': 0.35,
'paypal_fee_int_var': 3.9,
'paypal_api_enabled': False,
}
def paypal_compute_fees(self, cr, uid, id, amount, country_id, context=None):
""" Compute paypal fees.
:param float amount: the amount to pay
:param integer country_id: an ID of a res.country, or None. This is
the customer's country, to be compared to
the acquirer company country.
:return float fees: computed fees
"""
acquirer = self.browse(cr, uid, id, context=context)
country = self.pool['res.country'].browse(cr, uid, country_id, context=context)
if country and acquirer.company_id.country_id.id == country.id:
fees = amount * (1 + acquirer.paypal_fee_dom_var / 100.0) + acquirer.paypal_fee_dom_fixed - amount
else:
fees = amount * (1 + acquirer.paypal_fee_int_var / 100.0) + acquirer.paypal_fee_int_fixed - amount
return fees
def paypal_form_generate_values(self, cr, uid, id, reference, amount, currency, partner_id=False, partner_values=None, tx_custom_values=None, context=None):
if partner_values is None:
partner_values = {}
@ -48,6 +77,11 @@ class AcquirerPaypal(osv.Model):
partner = None
if partner_id:
partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context)
if partner:
country_id = partner.country_id.id
else:
country_ids = self.pool['res.country'].search(cr, uid, [('name', '=', partner_values.get('country_name'))], context=context)
country_id = country_ids and country_ids[0] or None
tx_values = {
'cmd': '_xclick',
'business': acquirer.paypal_email_id,
@ -66,6 +100,8 @@ class AcquirerPaypal(osv.Model):
'notify_url': '%s' % urlparse.urljoin(base_url, PaypalController._notify_url),
'cancel_return': '%s' % urlparse.urljoin(base_url, PaypalController._cancel_url),
}
if acquirer.paypal_fee_active:
tx_values['handling'] = '%.2f' % self.paypal_compute_fees(cr, uid, acquirer.id, amount, country_id, context=context)
if tx_custom_values and tx_custom_values.get('return_url'):
tx_values['custom'] = json.dumps({'return_url': '%s' % tx_custom_values.pop('return_url')})
if tx_custom_values:
@ -112,6 +148,16 @@ class TxPaypal(osv.Model):
'paypal_txn_type': fields.char('Transaction type'),
}
# --------------------------------------------------
# CRUD
# --------------------------------------------------
def paypal_create(self, cr, uid, values, context=None):
paypal = self.pool['payment.acquirer'].browse(cr, uid, values['acquirer_id'], context=context)
if paypal.paypal_fee_active:
values['fees'] = self.pool['payment.acquirer'].paypal_compute_fees(cr, uid, paypal.id, values.get('amount', 0.0), values.get('country_id'), context=context)
return values
# --------------------------------------------------
# FORM RELATED METHODS
# --------------------------------------------------

View File

@ -95,6 +95,9 @@ class PaypalForm(PaypalCommon):
def test_10_paypal_form_render(self):
cr, uid, context = self.cr, self.uid, {}
# be sure not to do stupid things
paypal = self.payment_acquirer.browse(cr, uid, self.paypal_id, context)
self.assertEqual(paypal.env, 'test', 'test without test env')
# ----------------------------------------
# Test: button direct rendering
@ -139,9 +142,40 @@ class PaypalForm(PaypalCommon):
'paypal: wrong value for form: received %s instead of %s' % (form_input.get('value'), form_values[form_input.get('name')])
)
def test_11_paypal_form_with_fees(self):
cr, uid, context = self.cr, self.uid, {}
self.payment_acquirer.write(cr, uid, self.paypal_id, {
'paypal_fee_active': True,
}, context)
# be sure not to do stupid things
paypal = self.payment_acquirer.browse(self.cr, self.uid, self.paypal_id, None)
self.assertEqual(paypal.env, 'test', 'test without test env')
# render the button
res = self.payment_acquirer.render(
cr, uid, self.paypal_id,
'test_ref0', 12.50, self.currency_euro,
partner_id=None,
partner_values=self.buyer_values,
context=context)
# check form result
handling_found = False
tree = objectify.fromstring(res)
self.assertEqual(tree.get('action'), 'https://www.sandbox.paypal.com/cgi-bin/webscr', 'paypal: wrong form POST url')
for form_input in tree.input:
if form_input.get('name') in ['handling']:
handling_found = True
self.assertEqual(form_input.get('value'), '0.84', 'paypal: wrong computed fees')
self.assertTrue(handling_found, 'paypal: paypal_fee_active did not add handling input in rendered form')
@mute_logger('openerp.addons.payment_acquirer_paypal.models.paypal', 'ValidationError')
def test_20_paypal_form_management(self):
cr, uid, context = self.cr, self.uid, {}
# be sure not to do stupid things
paypal = self.payment_acquirer.browse(cr, uid, self.paypal_id, context)
self.assertEqual(paypal.env, 'test', 'test without test env')
# typical data posted by paypal after client has successfully paid
paypal_post_data = {

View File

@ -7,15 +7,29 @@
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment_acquirer.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer_base"]' position='after'>
<group string="Paypal Details"
attrs="{'invisible': [('name', '!=', 'paypal')]}">
<field name="paypal_email_id"/>
<field name="paypal_username"/>
<field name="paypal_use_ipn"/>
<field name="paypal_api_enabled"/>
<field name="paypal_api_username"/>
<field name="paypal_api_password"/>
<xpath expr='//group[@name="acquirer_display"]' position='after'>
<group attrs="{'invisible': [('name', '!=', 'paypal')]}">
<group>
<field name="paypal_email_id"/>
<field name="paypal_username"/>
<field name="paypal_use_ipn"/>
<field name="paypal_api_enabled"/>
<field name="paypal_api_username"
attrs="{'invisible': [('paypal_api_enabled', '=', False)]}"/>
<field name="paypal_api_password"
attrs="{'invisible': [('paypal_api_enabled', '=', False)]}"/>
</group>
<group>
<field name="paypal_fee_active"/>
<field name="paypal_fee_dom_fixed"
attrs="{'invisible': [('paypal_fee_active', '=', False)]}"/>
<field name="paypal_fee_dom_var"
attrs="{'invisible': [('paypal_fee_active', '=', False)]}"/>
<field name="paypal_fee_int_fixed"
attrs="{'invisible': [('paypal_fee_active', '=', False)]}"/>
<field name="paypal_fee_int_var"
attrs="{'invisible': [('paypal_fee_active', '=', False)]}"/>
</group>
</group>
</xpath>
</field>

View File

@ -9,6 +9,10 @@
<input type="hidden" name="item_name" t-att-value="tx_values['item_name']"/>
<input type="hidden" name="item_number" t-att-value="tx_values['item_number']"/>
<input type="hidden" name="amount" t-att-value="tx_values['amount']"/>
<t t-if="'handling' in tx_values">
<input type="hidden" name="handling" t-att-value="tx_values['handling']"/>
</t>
<input type="hidden" name="amount" t-att-value="tx_values['amount']"/>
<input type="hidden" name="currency_code" t-att-value="tx_values['currency_code']"/>
<!-- partner / address data -->
<input type="hidden" name="address1" t-att-value="tx_values['address1']"/>