2013-10-18 11:15:30 +00:00
|
|
|
# -*- coding: utf-'8' "-*-"
|
2013-10-10 14:24:15 +00:00
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# OpenERP, Open Source Management Solution
|
|
|
|
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
|
|
|
from openerp.osv import osv, fields
|
2013-10-24 14:20:50 +00:00
|
|
|
|
2013-10-17 11:00:46 +00:00
|
|
|
import logging
|
2013-10-10 14:24:15 +00:00
|
|
|
|
2013-10-17 11:00:46 +00:00
|
|
|
_logger = logging.getLogger(__name__)
|
2013-10-10 14:24:15 +00:00
|
|
|
|
2013-10-17 11:00:46 +00:00
|
|
|
|
2013-10-24 14:20:50 +00:00
|
|
|
class ValidationError(ValueError):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
class PaymentAcquirer(osv.Model):
|
2013-10-10 14:24:15 +00:00
|
|
|
_name = 'payment.acquirer'
|
|
|
|
_description = 'Online Payment Acquirer'
|
2013-10-24 14:20:50 +00:00
|
|
|
|
2013-10-10 14:24:15 +00:00
|
|
|
_columns = {
|
|
|
|
'name': fields.char('Name', required=True),
|
2013-11-07 17:15:58 +00:00
|
|
|
'view_template_id': fields.many2one('ir.ui.view', required=True),
|
|
|
|
'env': fields.selection(
|
|
|
|
[('test', 'Test'), ('prod', 'Production')],
|
|
|
|
string='Environment'),
|
|
|
|
'portal_published': fields.boolean('Visible in Portal',
|
|
|
|
help="Make this payment acquirer available (Customer invoices, etc.)"),
|
|
|
|
}
|
|
|
|
|
|
|
|
_defaults = {
|
|
|
|
'portal_published': True,
|
|
|
|
'env': 'test',
|
2013-10-10 14:24:15 +00:00
|
|
|
}
|
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
def _check_required_if_provider(self, cr, uid, ids, context=None):
|
|
|
|
for acquirer in self.browse(cr, uid, ids, context=context):
|
|
|
|
if any(c for c, f in self._all_columns.items() if getattr(f.column, 'required_if_provider', None) == acquirer.name and not acquirer[c]):
|
2013-10-18 11:15:30 +00:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
_constraints = [
|
2013-11-07 17:15:58 +00:00
|
|
|
(_check_required_if_provider, 'Required fields not filled', ['required for this provider']),
|
2013-10-18 11:15:30 +00:00
|
|
|
]
|
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
def render(self, cr, uid, id, reference, amount, currency, tx_id=None, partner_id=False, partner_values=None, tx_custom_values=None, context=None):
|
|
|
|
""" Renders the form template of the given acquirer as a qWeb template.
|
|
|
|
All templates should handle:
|
|
|
|
|
|
|
|
- acquirer: the payment.acquirer browse record
|
|
|
|
- user: the current user browse record
|
|
|
|
- currency: currency browse record
|
|
|
|
- amount: amount of the transaction
|
|
|
|
- reference: reference of the transaction
|
|
|
|
- partner: the current partner browse record, if any (not necessarily set)
|
|
|
|
- partner_values: a dictionary of partner-related values
|
|
|
|
- tx_values: a dictionary of transaction related values that depends on
|
|
|
|
on the acquirer
|
|
|
|
- context: OpenERP context dictionary
|
|
|
|
|
|
|
|
:param string reference: the transaction reference
|
|
|
|
:param float amount: the amount the buyer has to pay
|
|
|
|
:param res.currency browse record currency: currency
|
|
|
|
:param int tx_id: id of a transaction; if set, bypasses all other given
|
|
|
|
values and only render the already-stored transaction
|
|
|
|
:param res.partner browse record partner_id: the buyer
|
|
|
|
:param dict partner_values: a dictionary of values for the buyer (see above)
|
|
|
|
:param dict tx_custom_values: a dictionary of values for the transction
|
|
|
|
that is given to the acquirer-specific method
|
|
|
|
generating the form values
|
|
|
|
:param dict context: OpenERP context
|
|
|
|
"""
|
|
|
|
if context is None:
|
|
|
|
context = {}
|
|
|
|
partner = None
|
|
|
|
if partner_id:
|
|
|
|
partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context)
|
|
|
|
acquirer = self.browse(cr, uid, id, context=context)
|
|
|
|
method_name = '%s_form_generate_values' % (acquirer.name)
|
|
|
|
|
|
|
|
tx_values = {}
|
|
|
|
if tx_id and hasattr(self.pool['payment.transaction'], method_name):
|
|
|
|
method = getattr(self.pool['payment.transaction'], method_name)
|
|
|
|
tx_values = method(cr, uid, tx_id, tx_custom_values, context=context)
|
|
|
|
elif hasattr(self, method_name):
|
|
|
|
method = getattr(self, method_name)
|
|
|
|
tx_values = method(cr, uid, id, reference, amount, currency, partner_id, partner_values, tx_custom_values, context=context)
|
|
|
|
|
|
|
|
qweb_context = {
|
|
|
|
'acquirer': acquirer,
|
|
|
|
'user': self.pool.get("res.users").browse(cr, uid, uid, context=context),
|
|
|
|
'reference': reference,
|
|
|
|
'amount': amount,
|
|
|
|
'currency': currency,
|
|
|
|
'partner': partner,
|
|
|
|
'partner_values': partner_values,
|
|
|
|
'tx_values': tx_values,
|
|
|
|
'context': context,
|
|
|
|
}
|
|
|
|
return self.pool['ir.ui.view'].render(cr, uid, acquirer.view_template_id.id, qweb_context, engine='ir.qweb', context=context)
|
2013-10-10 14:24:15 +00:00
|
|
|
|
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
class PaymentTransaction(osv.Model):
|
|
|
|
_name = 'payment.transaction'
|
|
|
|
_inherit = ['mail.thread']
|
|
|
|
_order = 'id desc'
|
2013-10-16 15:08:37 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
_columns = {
|
|
|
|
'date_create': fields.datetime('Creation Date', readonly=True, required=True),
|
|
|
|
'date_validate': fields.datetime('Validation Date'),
|
|
|
|
'acquirer_id': fields.many2one(
|
|
|
|
'payment.acquirer', 'Acquirer',
|
|
|
|
required=True,
|
|
|
|
),
|
|
|
|
'type': fields.selection(
|
|
|
|
[('server2server', 'Server To Server'), ('form', 'Form')],
|
|
|
|
string='Type', required=True),
|
|
|
|
'state': fields.selection(
|
|
|
|
[('draft', 'Draft'), ('pending', 'Pending'),
|
|
|
|
('done', 'Done'), ('error', 'Error')],
|
|
|
|
'Status', required=True,
|
|
|
|
track_visiblity='onchange'),
|
|
|
|
'state_message': fields.text('Message',
|
|
|
|
help='Field used to store error and/or validation messages for information'),
|
|
|
|
# payment
|
|
|
|
'amount': fields.float('Amount', required=True,
|
|
|
|
help='Amount in cents',
|
|
|
|
track_visibility='always'),
|
|
|
|
'currency_id': fields.many2one('res.currency', 'Currency', required=True),
|
|
|
|
'reference': fields.char('Order Reference', required=True),
|
|
|
|
'name': fields.char('Item name'),
|
|
|
|
# duplicate partner / transaction data to store the values at transaction time
|
|
|
|
'partner_id': fields.many2one('res.partner', 'Partner'),
|
|
|
|
'partner_name': fields.char('Partner Name'),
|
|
|
|
'partner_lang': fields.char('Lang'),
|
|
|
|
'partner_email': fields.char('Email'),
|
|
|
|
'partner_zip': fields.char('Zip'),
|
|
|
|
'partner_address': fields.char('Address'),
|
|
|
|
'partner_city': fields.char('City'),
|
|
|
|
'partner_country_id': fields.many2one('res.country', 'Country'),
|
|
|
|
'partner_phone': fields.char('Phone'),
|
|
|
|
'partner_reference': fields.char('Buyer Reference'),
|
|
|
|
}
|
2013-10-18 11:15:30 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
_defaults = {
|
|
|
|
'date_create': fields.datetime.now,
|
|
|
|
'type': 'form',
|
|
|
|
'state': 'draft',
|
|
|
|
'partner_lang': 'en_US',
|
|
|
|
}
|
2013-10-10 14:24:15 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
def create(self, cr, uid, values, context=None):
|
|
|
|
if not 'name' in values and 'reference' in values:
|
|
|
|
values['name'] = values['reference']
|
2013-11-08 09:44:44 +00:00
|
|
|
if values.get('partner_id'): # @TDENOTE: not sure
|
2013-11-07 17:15:58 +00:00
|
|
|
values.update(self.on_change_partner_id(cr, uid, None, values.get('partner_id'), context=context)['values'])
|
|
|
|
|
2013-11-08 09:44:44 +00:00
|
|
|
# call custom create method if defined (i.e. ogone_create for ogone)
|
2013-11-07 17:15:58 +00:00
|
|
|
if values.get('acquirer_id'):
|
|
|
|
acquirer = self.pool['payment.acquirer'].browse(cr, uid, values.get('acquirer_id'), context=context)
|
|
|
|
custom_method_name = '%s_create' % acquirer.name
|
|
|
|
if hasattr(self, custom_method_name):
|
|
|
|
values.update(getattr(self, custom_method_name)(cr, uid, values, context=context))
|
|
|
|
|
|
|
|
return super(PaymentTransaction, self).create(cr, uid, values, context=context)
|
|
|
|
|
|
|
|
def on_change_partner_id(self, cr, uid, ids, partner_id, context=None):
|
|
|
|
if partner_id:
|
|
|
|
partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context)
|
|
|
|
values = {
|
|
|
|
'partner_name': partner.name,
|
|
|
|
'partner_lang': partner.lang,
|
|
|
|
'partner_email': partner.email,
|
|
|
|
'partner_zip': partner.zip,
|
|
|
|
'partner_address': ' '.join((partner.street, partner.street2)).strip(),
|
|
|
|
'partner_city': partner.city,
|
|
|
|
'partner_country_id': partner.country_id.id,
|
|
|
|
'partner_phone': partner.phone,
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
values = {
|
|
|
|
'partner_name': False,
|
|
|
|
'partner_lang': 'en_US',
|
|
|
|
'partner_email': False,
|
|
|
|
'partner_zip': False,
|
|
|
|
'partner_address': False,
|
|
|
|
'partner_city': False,
|
|
|
|
'partner_country_id': False,
|
|
|
|
'partner_phone': False,
|
|
|
|
}
|
|
|
|
return {'values': values}
|
2013-10-10 14:24:15 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
def validate(self, cr, uid, ids, context=None):
|
2013-10-17 11:00:46 +00:00
|
|
|
"""
|
2013-10-18 11:15:30 +00:00
|
|
|
return (status, retry_time, log)
|
|
|
|
status: "validated" or "refused" or "pending"
|
2013-10-17 11:00:46 +00:00
|
|
|
retry_time = False (don't retry validation) or int (seconds for retry validation)
|
2013-10-18 11:15:30 +00:00
|
|
|
log = str
|
2013-10-17 11:00:46 +00:00
|
|
|
"""
|
2013-11-07 17:15:58 +00:00
|
|
|
res = []
|
|
|
|
for tx in self.browse(cr, uid, ids, context=context):
|
|
|
|
method = getattr(self, '%s_validate')
|
|
|
|
status, retry_time, log = method(cr, uid, [tx.id], context=context)[0]
|
|
|
|
|
|
|
|
# log validation on transaction
|
|
|
|
self.message_post(
|
|
|
|
cr, uid, tx.id,
|
2013-10-18 11:15:30 +00:00
|
|
|
body=log or "",
|
|
|
|
subject="%s%s" % (status, retry_time and ": %s" % retry_time or ""),
|
|
|
|
type='notification',
|
2013-10-24 14:20:50 +00:00
|
|
|
context=context
|
|
|
|
)
|
2013-10-18 11:15:30 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
if status == "validated":
|
|
|
|
_logger.info("Tx Validated for %s:%s" % (tx.acquirer_id.name, tx.reference))
|
|
|
|
elif status == "pending":
|
|
|
|
_logger.debug("Tx Pending for %s:%s. Reason: %s" % (tx.acquirer_id.name, tx.reference))
|
2013-10-18 11:15:30 +00:00
|
|
|
else:
|
2013-11-07 17:15:58 +00:00
|
|
|
_logger.error("Tx Refused for %s:%s. Reason: %s" % (tx.acquirer_id.name, tx.reference))
|
2013-10-18 11:15:30 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
res.append((status, retry_time, log))
|
2013-10-18 11:15:30 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
return res
|
2013-10-18 11:15:30 +00:00
|
|
|
|
2013-11-07 17:15:58 +00:00
|
|
|
def create_s2s(self, cr, uid, context=None):
|
|
|
|
pass
|