mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[IMP] import module to integrate with HSBC
This commit is contained in:
24
account_banking_uk_hsbc/__init__.py
Normal file
24
account_banking_uk_hsbc/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import account_banking_uk_hsbc
|
||||
import wizard
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
45
account_banking_uk_hsbc/__openerp__.py
Normal file
45
account_banking_uk_hsbc/__openerp__.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
{
|
||||
'name': 'HSBC Account Banking',
|
||||
'version': '0.1',
|
||||
'license': 'GPL-3',
|
||||
'author': 'credativ Ltd',
|
||||
'website': 'http://www.credativ.co.uk',
|
||||
'category': 'Account Banking',
|
||||
'depends': ['account_banking'],
|
||||
'init_xml': [],
|
||||
'update_xml': [
|
||||
'account_banking_uk_hsbc.xml',
|
||||
'data/banking_export_hsbc.xml',
|
||||
'wizard/export_hsbc_view.xml',
|
||||
],
|
||||
'demo_xml': [],
|
||||
'description': '''
|
||||
Module to import HSBC format transation files and to export payments for HSBC.net.
|
||||
|
||||
Currently targetting UK market.
|
||||
|
||||
This modules contains no logic, just an import/export filter for account_banking.
|
||||
''',
|
||||
'active': False,
|
||||
'installable': True,
|
||||
}
|
||||
65
account_banking_uk_hsbc/account_banking_uk_hsbc.py
Normal file
65
account_banking_uk_hsbc/account_banking_uk_hsbc.py
Normal file
@@ -0,0 +1,65 @@
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import osv, fields
|
||||
from datetime import date
|
||||
from tools.translate import _
|
||||
|
||||
class hsbc_export(osv.osv):
|
||||
'''HSBC Export'''
|
||||
_name = 'banking.export.hsbc'
|
||||
_description = __doc__
|
||||
_rec_name = 'execution_date'
|
||||
|
||||
_columns = {
|
||||
'payment_order_ids': fields.many2many(
|
||||
'payment.order',
|
||||
'account_payment_order_hsbc_rel',
|
||||
'banking_export_hsbc_id', 'account_order_id',
|
||||
'Payment Orders',
|
||||
readonly=True),
|
||||
'identification':
|
||||
fields.char('Identification', size=15, readonly=True, select=True),
|
||||
'execution_date':
|
||||
fields.date('Execution Date',readonly=True),
|
||||
'no_transactions':
|
||||
fields.integer('Number of Transactions', readonly=True),
|
||||
'total_amount':
|
||||
fields.float('Total Amount', readonly=True),
|
||||
'date_generated':
|
||||
fields.datetime('Generation Date', readonly=True, select=True),
|
||||
'file':
|
||||
fields.binary('HSBC File', readonly=True),
|
||||
'state':
|
||||
fields.selection([
|
||||
('draft', 'Draft'),
|
||||
('sent', 'Sent'),
|
||||
('done', 'Reconciled'),
|
||||
], 'State', readonly=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'date_generated': lambda *a: date.today().strftime('%Y-%m-%d'),
|
||||
'state': lambda *a: 'draft',
|
||||
}
|
||||
hsbc_export()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
85
account_banking_uk_hsbc/account_banking_uk_hsbc.xml
Normal file
85
account_banking_uk_hsbc/account_banking_uk_hsbc.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) EduSense BV <http://www.edusense.nl>
|
||||
All rights reserved.
|
||||
|
||||
Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>)
|
||||
|
||||
The licence is in the file __openerp__.py
|
||||
|
||||
|
||||
-->
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Make new view on HSBC exports -->
|
||||
<record id="view_banking_export_hsbc_form" model="ir.ui.view">
|
||||
<field name="name">account.banking.export.hsbc.form</field>
|
||||
<field name="model">banking.export.hsbc</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="HSBC Export">
|
||||
<notebook>
|
||||
<page string="General Information">
|
||||
<separator string="HSBC Information" colspan="4" />
|
||||
<field name="total_amount" />
|
||||
<field name="no_transactions" />
|
||||
<separator string="Processing Information" colspan="4" />
|
||||
<field name="execution_date" />
|
||||
<field name="date_generated" />
|
||||
<newline />
|
||||
<field name="file" colspan="4" />
|
||||
</page>
|
||||
<page string="Payment Orders">
|
||||
<field name="payment_order_ids" colspan="4" nolabel="1">
|
||||
<tree colors="blue:state in ('draft');gray:state in ('cancel','done');black:state in ('open')" string="Payment order">
|
||||
<field name="reference"/>
|
||||
<field name="date_created"/>
|
||||
<field name="date_done"/>
|
||||
<field name="total"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_banking_export_hsbc_tree" model="ir.ui.view">
|
||||
<field name="name">account.banking.export.hsbc.tree</field>
|
||||
<field name="model">banking.export.hsbc</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="HSBC Export">
|
||||
<field name="execution_date" search="2"/>
|
||||
<field name="date_generated" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_account_banking_hsbcs">
|
||||
<field name="name">Generated HSBC files</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">banking.export.hsbc</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- Add a menu item for it -->
|
||||
<menuitem name="Generated HSBC files"
|
||||
id="menu_action_account_banking_exported_hsbc_files"
|
||||
parent="account_banking.menu_finance_banking_actions"
|
||||
action="action_account_banking_hsbcs"
|
||||
sequence="12"
|
||||
/>
|
||||
|
||||
<!-- Create right menu entry to see generated files -->
|
||||
<act_window name="Generated HSBC files"
|
||||
domain="[('payment_order_ids', '=', active_id)]"
|
||||
res_model="banking.export.hsbc"
|
||||
src_model="payment.order"
|
||||
view_type="form"
|
||||
view_mode="tree,form"
|
||||
id="act_banking_export_hsbc_payment_order"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
13
account_banking_uk_hsbc/data/banking_export_hsbc.xml
Normal file
13
account_banking_uk_hsbc/data/banking_export_hsbc.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="payment.mode.type" id="export_hsbc">
|
||||
<field name="name">HSBC</field>
|
||||
<field name="code">HSBC</field>
|
||||
<field name="suitable_bank_types"
|
||||
eval="[(6,0,[ref('base_iban.bank_iban'),ref('base.bank_normal'),])]" />
|
||||
<field name="ir_model_id"
|
||||
ref="account_banking_uk_hsbc.model_banking_export_hsbc_wizard"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
23
account_banking_uk_hsbc/wizard/__init__.py
Normal file
23
account_banking_uk_hsbc/wizard/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import export_hsbc
|
||||
339
account_banking_uk_hsbc/wizard/export_hsbc.py
Normal file
339
account_banking_uk_hsbc/wizard/export_hsbc.py
Normal file
@@ -0,0 +1,339 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import base64
|
||||
from datetime import datetime, date, timedelta
|
||||
from osv import osv, fields
|
||||
from tools.translate import _
|
||||
from decimal import Decimal
|
||||
import paymul
|
||||
import string
|
||||
import random
|
||||
|
||||
def strpdate(arg, format='%Y-%m-%d'):
|
||||
'''shortcut'''
|
||||
return datetime.strptime(arg, format).date()
|
||||
|
||||
def strfdate(arg, format='%Y-%m-%d'):
|
||||
'''shortcut'''
|
||||
return arg.strftime(format)
|
||||
|
||||
class banking_export_hsbc_wizard(osv.osv_memory):
|
||||
_name = 'banking.export.hsbc.wizard'
|
||||
_description = 'HSBC Export'
|
||||
_columns = {
|
||||
'state': fields.selection(
|
||||
[
|
||||
('create', 'Create'),
|
||||
('finish', 'Finish')
|
||||
],
|
||||
'State',
|
||||
readonly=True,
|
||||
),
|
||||
'test': fields.boolean(),
|
||||
'reference': fields.char(
|
||||
'Reference', size=35,
|
||||
help=('The bank will use this reference in feedback communication '
|
||||
'to refer to this run. 35 characters are available.'
|
||||
),
|
||||
),
|
||||
'execution_date_create': fields.date(
|
||||
'Execution Date',
|
||||
help=('This is the date the file should be processed by the bank. '
|
||||
'Don\'t choose a date beyond the nearest date in your '
|
||||
'payments. The latest allowed date is 30 days from now.\n'
|
||||
'Please keep in mind that banks only execute on working days '
|
||||
'and typically use a delay of two days between execution date '
|
||||
'and effective transfer date.'
|
||||
),
|
||||
),
|
||||
'file_id': fields.many2one(
|
||||
'banking.export.hsbc',
|
||||
'hsbc File',
|
||||
readonly=True
|
||||
),
|
||||
'file': fields.related(
|
||||
'file_id', 'file', type='binary',
|
||||
readonly=True,
|
||||
string='File',
|
||||
),
|
||||
'execution_date_finish': fields.related(
|
||||
'file_id', 'execution_date', type='date',
|
||||
readonly=True,
|
||||
string='Execution Date',
|
||||
),
|
||||
'total_amount': fields.related(
|
||||
'file_id', 'total_amount',
|
||||
type='float',
|
||||
string='Total Amount',
|
||||
readonly=True,
|
||||
),
|
||||
'no_transactions': fields.integer(
|
||||
'Number of Transactions',
|
||||
readonly=True,
|
||||
),
|
||||
'payment_order_ids': fields.many2many(
|
||||
'payment.order', 'rel_wiz_payorders', 'wizard_id',
|
||||
'payment_order_id', 'Payment Orders',
|
||||
readonly=True,
|
||||
),
|
||||
}
|
||||
|
||||
def create(self, cursor, uid, wizard_data, context=None):
|
||||
'''
|
||||
Retrieve a sane set of default values based on the payment orders
|
||||
from the context.
|
||||
'''
|
||||
|
||||
if not 'execution_date_create' in wizard_data:
|
||||
po_ids = context.get('active_ids', [])
|
||||
po_model = self.pool.get('payment.order')
|
||||
pos = po_model.browse(cursor, uid, po_ids)
|
||||
|
||||
execution_date = date.today()
|
||||
|
||||
for po in pos:
|
||||
if po.date_prefered == 'fixed' and po.date_planned:
|
||||
execution_date = strpdate(po.date_planned)
|
||||
elif po.date_prefered == 'due':
|
||||
for line in po.line_ids:
|
||||
if line.move_line_id.date_maturity:
|
||||
date_maturity = strpdate(line.move_line_id.date_maturity)
|
||||
if date_maturity < execution_date:
|
||||
execution_date = date_maturity
|
||||
|
||||
execution_date = max(execution_date, date.today())
|
||||
|
||||
# The default reference contains a /, which is invalid for PAYMUL
|
||||
reference = pos[0].reference.replace('/', ' ')
|
||||
|
||||
wizard_data.update({
|
||||
'execution_date_create': strfdate(execution_date),
|
||||
'reference': reference,
|
||||
'payment_order_ids': [[6, 0, po_ids]],
|
||||
'state': 'create',
|
||||
})
|
||||
|
||||
return super(banking_export_hsbc_wizard, self).create(
|
||||
cursor, uid, wizard_data, context)
|
||||
|
||||
def _create_account(self, oe_account):
|
||||
currency = None # let the receiving bank select the currency from the batch
|
||||
holder = oe_account.owner_name or oe_account.partner_id.name
|
||||
|
||||
if oe_account.iban:
|
||||
paymul_account = paymul.IBANAccount(
|
||||
iban=oe_account.iban,
|
||||
bic=oe_account.bank.bic,
|
||||
holder=holder,
|
||||
currency=currency,
|
||||
)
|
||||
transaction_kwargs = {
|
||||
'charges': paymul.CHARGES_EACH_OWN,
|
||||
'means': paymul.MEANS_EZONE,
|
||||
}
|
||||
elif oe_account.country_id.code == 'GB':
|
||||
sortcode, accountno = oe_account.acc_number.split(" ", 2)
|
||||
|
||||
paymul_account = paymul.UKAccount(
|
||||
number=accountno,
|
||||
sortcode=sortcode,
|
||||
holder=holder,
|
||||
currency=currency,
|
||||
)
|
||||
transaction_kwargs = {
|
||||
'charges': paymul.CHARGES_PAYEE,
|
||||
'means': paymul.MEANS_ACH,
|
||||
}
|
||||
else:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('%s: only UK accounts and IBAN are supported') % (holder)
|
||||
)
|
||||
|
||||
return paymul_account, transaction_kwargs
|
||||
|
||||
def _create_transaction(self, line):
|
||||
# Check on missing partner of bank account (this can happen!)
|
||||
if not line.bank_id or not line.bank_id.partner_id:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('There is insufficient information.\r\n'
|
||||
'Both destination address and account '
|
||||
'number must be provided'
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
dest_account, transaction_kwargs = self._create_account(
|
||||
line.bank_id)
|
||||
except ValueError as exc:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('Destination account invalid: ') + str(exc)
|
||||
)
|
||||
|
||||
try:
|
||||
return paymul.Transaction(
|
||||
amount=Decimal(str(line.amount_currency)),
|
||||
currency=line.currency.name,
|
||||
account=dest_account,
|
||||
name_address=line.info_partner,
|
||||
customer_reference=line.name,
|
||||
payment_reference=line.name,
|
||||
**transaction_kwargs
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('Transaction invalid: ') + str(exc)
|
||||
)
|
||||
|
||||
def wizard_export(self, cursor, uid, wizard_data_ids, context):
|
||||
'''
|
||||
Wizard to actually create the HSBC file
|
||||
'''
|
||||
|
||||
wizard_data = self.browse(cursor, uid, wizard_data_ids, context)[0]
|
||||
result_model = self.pool.get('banking.export.hsbc')
|
||||
payment_orders = wizard_data.payment_order_ids
|
||||
|
||||
|
||||
try:
|
||||
src_account = self._create_account(
|
||||
payment_orders[0].mode.bank_id,
|
||||
)[0]
|
||||
except ValueError as exc:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('Source account invalid: ') + str(exc)
|
||||
)
|
||||
|
||||
if not isinstance(src_account, paymul.UKAccount):
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_("Your company's bank account has to have a valid UK "
|
||||
"account number (not IBAN)" + str(type(src_account)))
|
||||
)
|
||||
|
||||
transactions = []
|
||||
for po in payment_orders:
|
||||
transactions += [self._create_transaction(l) for l in po.line_ids]
|
||||
|
||||
try:
|
||||
batch = paymul.Batch(
|
||||
exec_date=strpdate(wizard_data.execution_date_create),
|
||||
reference=wizard_data.reference,
|
||||
debit_account=src_account,
|
||||
name_address=payment_orders[0].line_ids[0].info_owner,
|
||||
)
|
||||
batch.transactions = transactions
|
||||
except ValueError as exc:
|
||||
raise osv.except_osv(
|
||||
_('Error'),
|
||||
_('Batch invalid: ') + str(exc)
|
||||
)
|
||||
|
||||
# Generate random identifier until an unused one is found
|
||||
while True:
|
||||
ref = ''.join(random.choice(string.ascii_uppercase + string.digits)
|
||||
for x in range(15))
|
||||
|
||||
ids = result_model.search(cursor, uid, [
|
||||
('identification', '=', ref)
|
||||
])
|
||||
|
||||
if not ids:
|
||||
break
|
||||
|
||||
message = paymul.Message(reference=ref)
|
||||
message.batches.append(batch)
|
||||
interchange = paymul.Interchange(client_id='CLIENTID',
|
||||
reference=ref,
|
||||
message=message)
|
||||
|
||||
export_result = {
|
||||
'identification': interchange.reference,
|
||||
'execution_date': batch.exec_date,
|
||||
'total_amount': batch.amount(),
|
||||
'no_transactions': len(batch.transactions),
|
||||
'file': base64.encodestring(str(interchange)),
|
||||
'payment_order_ids': [
|
||||
[6, 0, [po.id for po in payment_orders]]
|
||||
],
|
||||
}
|
||||
file_id = result_model.create(cursor, uid, export_result, context)
|
||||
|
||||
self.write(cursor, uid, [wizard_data_ids[0]], {
|
||||
'file_id': file_id,
|
||||
'no_transactions' : len(batch.transactions),
|
||||
'state': 'finish',
|
||||
}, context)
|
||||
|
||||
return {
|
||||
'name': _('HSBC Export'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': self._name,
|
||||
'domain': [],
|
||||
'context': dict(context, active_ids=wizard_data_ids),
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'res_id': wizard_data_ids[0] or False,
|
||||
}
|
||||
|
||||
def wizard_cancel(self, cursor, uid, ids, context):
|
||||
'''
|
||||
Cancel the export: just drop the file
|
||||
'''
|
||||
|
||||
wizard_data = self.browse(cursor, uid, ids, context)[0]
|
||||
result_model = self.pool.get('banking.export.hsbc')
|
||||
|
||||
try:
|
||||
result_model.unlink(cursor, uid, wizard_data.file_id.id)
|
||||
except AttributeError:
|
||||
# file_id missing, wizard storage gone, server was restarted
|
||||
pass
|
||||
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def wizard_save(self, cursor, uid, ids, context):
|
||||
'''
|
||||
Save the export: mark all payments in the file as 'sent'
|
||||
'''
|
||||
|
||||
wizard_data = self.browse(cursor, uid, ids, context)[0]
|
||||
result_model = self.pool.get('banking.export.hsbc')
|
||||
po_model = self.pool.get('payment.order')
|
||||
|
||||
result_model.write(cursor, uid, [wizard_data.file_id.id],
|
||||
{'state':'sent'})
|
||||
|
||||
po_ids = [po.id for po in wizard_data.payment_order_ids]
|
||||
po_model.action_sent(cursor, uid, po_ids)
|
||||
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
banking_export_hsbc_wizard()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
37
account_banking_uk_hsbc/wizard/export_hsbc_view.xml
Normal file
37
account_banking_uk_hsbc/wizard/export_hsbc_view.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="wizard_banking_export_wizard_view" model="ir.ui.view">
|
||||
<field name="name">banking.export.hsbc.wizard.view</field>
|
||||
<field name="model">banking.export.hsbc.wizard</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="HSBC Export">
|
||||
<field name="state" invisible="True"/>
|
||||
<group states="create">
|
||||
<separator colspan="4" string="Processing Details"/>
|
||||
<field name="execution_date_create"/>
|
||||
<field name="test"/>
|
||||
<separator colspan="4" string="Reference for further communication"/>
|
||||
<field name="reference" colspan="2"/>
|
||||
<separator colspan="4" string="Additional message for all transactions"/>
|
||||
<newline/>
|
||||
<button icon="gtk-close" special="cancel" string="Cancel"/>
|
||||
<button icon="gtk-ok" string="Export" name="wizard_export" type="object"/>
|
||||
</group>
|
||||
<group states="finish">
|
||||
<field name="total_amount"/>
|
||||
<field name="no_transactions"/>
|
||||
<field name="execution_date_finish"/>
|
||||
<newline/>
|
||||
<!--<field name="file_id"/>-->
|
||||
<field name="file"/>
|
||||
<newline/>
|
||||
<button icon="gtk-close" string="Cancel" name="wizard_cancel" type="object"/>
|
||||
<button icon="gtk-ok" string="Finish" name="wizard_save" type="object"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
473
account_banking_uk_hsbc/wizard/paymul.py
Normal file
473
account_banking_uk_hsbc/wizard/paymul.py
Normal file
@@ -0,0 +1,473 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from account_banking import sepa
|
||||
from decimal import Decimal
|
||||
import datetime
|
||||
import re
|
||||
|
||||
def split_account_holder(holder):
|
||||
holder_parts = holder.split("\n")
|
||||
|
||||
try:
|
||||
line2 = holder_parts[1]
|
||||
except IndexError:
|
||||
line2 = ''
|
||||
|
||||
return holder_parts[0], line2
|
||||
|
||||
"""
|
||||
The standard says alphanumeric characters, but spaces are also allowed
|
||||
"""
|
||||
def edifact_isalnum(s):
|
||||
return bool(re.match(r'^[A-Za-z0-9 ]*$', s))
|
||||
|
||||
def edifact_digits(val, digits, mindigits=None):
|
||||
if mindigits is None:
|
||||
mindigits = digits
|
||||
|
||||
pattern = r'^[0-9]{' + str(mindigits) + ',' + str(digits) + r'}$'
|
||||
return bool(re.match(pattern, str(val)))
|
||||
|
||||
class HasCurrency(object):
|
||||
def _get_currency(self):
|
||||
return self._currency
|
||||
|
||||
def _set_currency(self, currency):
|
||||
if currency is None:
|
||||
self._currency = None
|
||||
else:
|
||||
if not len(currency) <= 3:
|
||||
raise ValueError("Currency must be <= 3 characters long: " +
|
||||
str(currency))
|
||||
|
||||
if not edifact_isalnum(currency):
|
||||
raise ValueError("Currency must be alphanumeric: " + str(currency))
|
||||
|
||||
self._currency = currency.upper()
|
||||
|
||||
currency = property(_get_currency, _set_currency)
|
||||
|
||||
|
||||
class LogicalSection(object):
|
||||
|
||||
def __str__(self):
|
||||
segments = self.segments()
|
||||
|
||||
def format_segment(segment):
|
||||
return '+'.join([':'.join([str(y) for y in x]) for x in segment]) + "'"
|
||||
|
||||
return "\n".join([format_segment(s) for s in segments])
|
||||
|
||||
|
||||
def _fii_segment(self, party_qualifier):
|
||||
holder = split_account_holder(self.holder)
|
||||
account_identification = [self.number, holder[0]]
|
||||
if holder[1] or self.currency:
|
||||
account_identification.append(holder[1])
|
||||
if self.currency:
|
||||
account_identification.append(self.currency)
|
||||
return [
|
||||
['FII'],
|
||||
[party_qualifier],
|
||||
account_identification,
|
||||
self.institution_identification,
|
||||
[self.country],
|
||||
]
|
||||
|
||||
|
||||
class UKAccount(HasCurrency):
|
||||
def _get_number(self):
|
||||
return self._number
|
||||
|
||||
def _set_number(self, number):
|
||||
if not edifact_digits(number, 8):
|
||||
raise ValueError("Account number must be 8 digits long: " +
|
||||
str(number))
|
||||
|
||||
self._number = number
|
||||
|
||||
number = property(_get_number, _set_number)
|
||||
|
||||
def _get_sortcode(self):
|
||||
return self._sortcode
|
||||
|
||||
def _set_sortcode(self, sortcode):
|
||||
if not edifact_digits(sortcode, 6):
|
||||
raise ValueError("Account sort code must be 6 digits long: " +
|
||||
str(sortcode))
|
||||
|
||||
self._sortcode = sortcode
|
||||
|
||||
sortcode = property(_get_sortcode, _set_sortcode)
|
||||
|
||||
def _get_holder(self):
|
||||
return self._holder
|
||||
|
||||
def _set_holder(self, holder):
|
||||
holder_parts = split_account_holder(holder)
|
||||
|
||||
if not len(holder_parts[0]) <= 35:
|
||||
raise ValueError("Account holder must be <= 35 characters long")
|
||||
|
||||
if not len(holder_parts[1]) <= 35:
|
||||
raise ValueError("Second line of account holder must be <= 35 characters long")
|
||||
|
||||
if not edifact_isalnum(holder_parts[0]):
|
||||
raise ValueError("Account holder must be alphanumeric")
|
||||
|
||||
if not edifact_isalnum(holder_parts[1]):
|
||||
raise ValueError("Second line of account holder must be alphanumeric")
|
||||
|
||||
self._holder = holder.upper()
|
||||
|
||||
holder = property(_get_holder, _set_holder)
|
||||
|
||||
def __init__(self, number, holder, currency, sortcode):
|
||||
self.number = number
|
||||
self.holder = holder
|
||||
self.currency = currency
|
||||
self.sortcode = sortcode
|
||||
self.country = 'GB'
|
||||
self.institution_identification = ['', '', '', self.sortcode, 154, 133]
|
||||
|
||||
def fii_bf_segment(self):
|
||||
return _fii_segment(self, 'BF')
|
||||
|
||||
def fii_or_segment(self):
|
||||
return _fii_segment(self, 'OR')
|
||||
|
||||
class IBANAccount(HasCurrency):
|
||||
def _get_iban(self):
|
||||
return self._iban
|
||||
|
||||
def _set_iban(self, iban):
|
||||
iban_obj = sepa.IBAN(iban)
|
||||
if not iban_obj.valid:
|
||||
raise ValueError("IBAN is invalid")
|
||||
|
||||
self._iban = iban
|
||||
self.country = iban_obj.countrycode
|
||||
|
||||
iban = property(_get_iban, _set_iban)
|
||||
|
||||
def __init__(self, iban, bic, currency, holder):
|
||||
self.iban = iban
|
||||
self.number = iban
|
||||
self.bic = bic
|
||||
self.currency = currency
|
||||
self.holder = holder
|
||||
self.institution_identification = [self.bic, 25, 5, '', '', '' ]
|
||||
|
||||
def fii_bf_segment(self):
|
||||
return _fii_segment(self, 'BF')
|
||||
|
||||
class Interchange(LogicalSection):
|
||||
def _get_reference(self):
|
||||
return self._reference
|
||||
|
||||
def _set_reference(self, reference):
|
||||
if not len(reference) <= 15:
|
||||
raise ValueError("Reference must be <= 15 characters long")
|
||||
|
||||
if not edifact_isalnum(reference):
|
||||
raise ValueError("Reference must be alphanumeric")
|
||||
|
||||
self._reference = reference.upper()
|
||||
|
||||
reference = property(_get_reference, _set_reference)
|
||||
|
||||
def __init__(self, client_id, reference, create_dt=None, message=None):
|
||||
self.client_id = client_id
|
||||
self.create_dt = create_dt or datetime.datetime.now()
|
||||
self.reference = reference
|
||||
self.message = message
|
||||
|
||||
def segments(self):
|
||||
segments = []
|
||||
segments.append([
|
||||
['UNB'],
|
||||
['UNOA', 3],
|
||||
['', '', self.client_id],
|
||||
['', '', 'HEXAGON ABC'],
|
||||
[self.create_dt.strftime('%y%m%d'), self.create_dt.strftime('%H%M')],
|
||||
[self.reference],
|
||||
])
|
||||
segments += self.message.segments()
|
||||
segments.append([
|
||||
['UNZ'],
|
||||
[1],
|
||||
[self.reference],
|
||||
])
|
||||
return segments
|
||||
|
||||
|
||||
class Message(LogicalSection):
|
||||
def _get_reference(self):
|
||||
return self._reference
|
||||
|
||||
def _set_reference(self, reference):
|
||||
if not len(reference) <= 35:
|
||||
raise ValueError("Reference must be <= 35 characters long")
|
||||
|
||||
if not edifact_isalnum(reference):
|
||||
raise ValueError("Reference must be alphanumeric")
|
||||
|
||||
self._reference = reference.upper()
|
||||
|
||||
reference = property(_get_reference, _set_reference)
|
||||
|
||||
def __init__(self, reference, dt=None):
|
||||
if dt:
|
||||
self.dt = dt
|
||||
else:
|
||||
self.dt = datetime.datetime.now()
|
||||
|
||||
self.reference = reference
|
||||
self.batches = []
|
||||
|
||||
def segments(self):
|
||||
# HSBC only accepts one message per interchange
|
||||
message_reference_number = 1
|
||||
|
||||
segments = []
|
||||
|
||||
segments.append([
|
||||
['UNH'],
|
||||
[message_reference_number],
|
||||
['PAYMUL', 'D', '96A', 'UN', 'FUN01G'],
|
||||
])
|
||||
segments.append([
|
||||
['BGM'],
|
||||
[452],
|
||||
[self.reference],
|
||||
[9],
|
||||
])
|
||||
segments.append([
|
||||
['DTM'],
|
||||
(137, self.dt.strftime('%Y%m%d'), 102),
|
||||
])
|
||||
for index, batch in enumerate(self.batches):
|
||||
segments += batch.segments(index + 1)
|
||||
segments.append([
|
||||
['CNT'],
|
||||
['39', sum([len(x.transactions) for x in self.batches])],
|
||||
])
|
||||
segments.append([
|
||||
['UNT'],
|
||||
[len(segments) + 1],
|
||||
[message_reference_number]
|
||||
])
|
||||
|
||||
return segments
|
||||
|
||||
class Batch(LogicalSection):
|
||||
def _get_reference(self):
|
||||
return self._reference
|
||||
|
||||
def _set_reference(self, reference):
|
||||
if not len(reference) <= 18:
|
||||
raise ValueError("Reference must be <= 18 characters long")
|
||||
|
||||
if not edifact_isalnum(reference):
|
||||
raise ValueError("Reference must be alphanumeric")
|
||||
|
||||
self._reference = reference.upper()
|
||||
|
||||
reference = property(_get_reference, _set_reference)
|
||||
|
||||
def __init__(self, exec_date, reference, debit_account, name_address):
|
||||
self.exec_date = exec_date
|
||||
self.reference = reference
|
||||
self.debit_account = debit_account
|
||||
self.name_address = name_address
|
||||
self.transactions = []
|
||||
|
||||
def amount(self):
|
||||
return sum([x.amount for x in self.transactions])
|
||||
|
||||
def segments(self, index):
|
||||
if not edifact_digits(index, 6, 1):
|
||||
raise ValueError("Index must be 6 digits or less")
|
||||
|
||||
segments = []
|
||||
|
||||
segments.append([
|
||||
['LIN'],
|
||||
[index],
|
||||
])
|
||||
segments.append([
|
||||
['DTM'],
|
||||
[203, self.exec_date.strftime('%Y%m%d'), 102],
|
||||
])
|
||||
segments.append([
|
||||
['RFF'],
|
||||
['AEK', self.reference],
|
||||
])
|
||||
|
||||
currencies = set([x.currency for x in self.transactions])
|
||||
if len(currencies) > 1:
|
||||
raise ValueError("All transactions in a batch must have the same currency")
|
||||
|
||||
segments.append([
|
||||
['MOA'],
|
||||
[9, self.amount().quantize(Decimal('0.00')), currencies.pop()],
|
||||
])
|
||||
segments.append(self.debit_account.fii_or_segment())
|
||||
segments.append([
|
||||
['NAD'],
|
||||
['OY'],
|
||||
[''],
|
||||
self.name_address.upper().split("\n")[0:5],
|
||||
])
|
||||
|
||||
for index, transaction in enumerate(self.transactions):
|
||||
segments += transaction.segments(index + 1)
|
||||
|
||||
return segments
|
||||
|
||||
# From the spec for FCA segments:
|
||||
# 13 = All charges borne by payee (or beneficiary)
|
||||
# 14 = Each pay own cost
|
||||
# 15 = All charges borne by payor (or ordering customer)
|
||||
# For Faster Payments this should always be ‘14’
|
||||
# Where this field is not present, “14” will be used as a default.
|
||||
CHARGES_PAYEE = 13
|
||||
CHARGES_EACH_OWN = 14
|
||||
CHARGES_PAYER = 15
|
||||
|
||||
MEANS_ACH = 2
|
||||
MEANS_EZONE = 2
|
||||
MEANS_PRIORITY_PAYMENT = 52
|
||||
|
||||
CHANNEL_INTRA_COMPANY = 'Z24'
|
||||
|
||||
class Transaction(LogicalSection, HasCurrency):
|
||||
def _get_amount(self):
|
||||
return self._amount
|
||||
|
||||
def _set_amount(self, amount):
|
||||
if len(str(amount)) > 18:
|
||||
raise ValueError("Amount must be shorter than 18 bytes")
|
||||
|
||||
self._amount = amount
|
||||
|
||||
amount = property(_get_amount, _set_amount)
|
||||
|
||||
def _get_payment_reference(self):
|
||||
return self._payment_reference
|
||||
|
||||
def _set_payment_reference(self, payment_reference):
|
||||
if not len(payment_reference) <= 18:
|
||||
raise ValueError("Payment reference must be <= 18 characters long")
|
||||
|
||||
if not edifact_isalnum(payment_reference):
|
||||
raise ValueError("Payment reference must be alphanumeric")
|
||||
|
||||
self._payment_reference = payment_reference.upper()
|
||||
|
||||
payment_reference = property(_get_payment_reference, _set_payment_reference)
|
||||
|
||||
def _get_customer_reference(self):
|
||||
return self._customer_reference
|
||||
|
||||
def _set_customer_reference(self, customer_reference):
|
||||
if not len(customer_reference) <= 18:
|
||||
raise ValueError("Customer reference must be <= 18 characters long")
|
||||
|
||||
if not edifact_isalnum(customer_reference):
|
||||
raise ValueError("Customer reference must be alphanumeric")
|
||||
|
||||
self._customer_reference = customer_reference.upper()
|
||||
|
||||
customer_reference = property(_get_customer_reference, _set_customer_reference)
|
||||
|
||||
def __init__(self, amount, currency, account, means,
|
||||
name_address=None, party_name=None, channel='',
|
||||
charges=CHARGES_EACH_OWN, customer_reference=None,
|
||||
payment_reference=None):
|
||||
self.amount = amount
|
||||
self.currency = currency
|
||||
self.account = account
|
||||
self.name_address = name_address
|
||||
self.party_name = party_name
|
||||
self.means = means
|
||||
self.channel = channel
|
||||
self.charges = charges
|
||||
self.payment_reference = payment_reference
|
||||
self.customer_reference = customer_reference
|
||||
|
||||
def segments(self, index):
|
||||
segments = []
|
||||
segments.append([
|
||||
['SEQ'],
|
||||
[''],
|
||||
[index],
|
||||
])
|
||||
segments.append([
|
||||
['MOA'],
|
||||
[9, self.amount.quantize(Decimal('0.00')), self.currency],
|
||||
])
|
||||
|
||||
if self.customer_reference:
|
||||
segments.append([
|
||||
['RFF'],
|
||||
['CR', self.customer_reference],
|
||||
])
|
||||
|
||||
if self.payment_reference:
|
||||
segments.append([
|
||||
['RFF'],
|
||||
['PQ', self.payment_reference],
|
||||
])
|
||||
|
||||
if self.channel:
|
||||
segments.append([
|
||||
['PAI'],
|
||||
['', '', self.means, '', '', self.channel],
|
||||
])
|
||||
else:
|
||||
segments.append([
|
||||
['PAI'],
|
||||
['', '', self.means],
|
||||
])
|
||||
|
||||
segments.append([
|
||||
['FCA'],
|
||||
[self.charges],
|
||||
])
|
||||
|
||||
segments.append(self.account.fii_bf_segment())
|
||||
|
||||
nad_segment = [
|
||||
['NAD'],
|
||||
['BE'],
|
||||
[''],
|
||||
]
|
||||
if self.name_address:
|
||||
nad_segment.append(self.name_address.upper().split("\n")[0:5])
|
||||
else:
|
||||
nad_segment.append('')
|
||||
if self.party_name:
|
||||
nad_segment.append(self.party_name.upper().split("\n")[0:5])
|
||||
segments.append(nad_segment)
|
||||
|
||||
return segments
|
||||
274
account_banking_uk_hsbc/wizard/paymul_test.py
Normal file
274
account_banking_uk_hsbc/wizard/paymul_test.py
Normal file
@@ -0,0 +1,274 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2011 credativ Ltd (<http://www.credativ.co.uk>).
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import datetime
|
||||
import unittest2 as unittest
|
||||
import paymul
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
class PaymulTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.maxDiff = None
|
||||
|
||||
def test_uk_high_value_priority_payment(self):
|
||||
# Changes from spec example: Removed DTM for transaction, HSBC ignores it (section 2.8.3)
|
||||
expected = \
|
||||
"""UNB+UNOA:3+::ABC00000001+::HEXAGON ABC+041111:1500+UKHIGHVALUE'
|
||||
UNH+1+PAYMUL:D:96A:UN:FUN01G'
|
||||
BGM+452+UKHIGHVALUE+9'
|
||||
DTM+137:20041111:102'
|
||||
LIN+1'
|
||||
DTM+203:20041112:102'
|
||||
RFF+AEK:UKHIGHVALUE'
|
||||
MOA+9:1.00:GBP'
|
||||
FII+OR+12345678:HSBC NET TEST::GBP+:::400515:154:133+GB'
|
||||
NAD+OY++HSBC BANK PLC:HSBC NET TEST:TEST:TEST:UNITED KINGDOM'
|
||||
SEQ++1'
|
||||
MOA+9:1.00:GBP'
|
||||
RFF+CR:CRUKHV5'
|
||||
RFF+PQ:PQUKHV5'
|
||||
PAI+::52:::Z24'
|
||||
FCA+13'
|
||||
FII+BF+87654321:XYX LTD FROM FII BF 1:BEN NAME 2:GBP+:::403124:154:133+GB'
|
||||
NAD+BE++SOME BANK PLC:HSBC NET TEST:TEST:TEST:UNITED KINGDOM'
|
||||
CNT+39:1'
|
||||
UNT+19+1'
|
||||
UNZ+1+UKHIGHVALUE'"""
|
||||
|
||||
src_account = paymul.UKAccount(number=12345678,
|
||||
holder='HSBC NET TEST',
|
||||
currency='GBP',
|
||||
sortcode=400515)
|
||||
|
||||
dest_account = paymul.UKAccount(number=87654321,
|
||||
holder="XYX LTD FROM FII BF 1\nBEN NAME 2",
|
||||
currency='GBP',
|
||||
sortcode=403124)
|
||||
|
||||
transaction = paymul.Transaction(amount=Decimal('1.00'),
|
||||
currency='GBP',
|
||||
account=dest_account,
|
||||
charges=paymul.CHARGES_PAYEE,
|
||||
means=paymul.MEANS_PRIORITY_PAYMENT,
|
||||
channel=paymul.CHANNEL_INTRA_COMPANY,
|
||||
name_address="SOME BANK PLC\nHSBC NET TEST\nTEST\nTEST\nUNITED KINGDOM",
|
||||
customer_reference='CRUKHV5',
|
||||
payment_reference='PQUKHV5')
|
||||
|
||||
batch = paymul.Batch(exec_date=datetime.date(2004, 11, 12),
|
||||
reference='UKHIGHVALUE',
|
||||
debit_account=src_account,
|
||||
name_address="HSBC BANK PLC\nHSBC NET TEST\nTEST\nTEST\nUNITED KINGDOM")
|
||||
batch.transactions.append(transaction)
|
||||
|
||||
message = paymul.Message(reference='UKHIGHVALUE',
|
||||
dt=datetime.datetime(2004, 11, 11))
|
||||
message.batches.append(batch)
|
||||
|
||||
interchange = paymul.Interchange(client_id='ABC00000001',
|
||||
reference='UKHIGHVALUE',
|
||||
create_dt=datetime.datetime(2004, 11, 11, 15, 00),
|
||||
message=message)
|
||||
|
||||
self.assertMultiLineEqual(expected, str(interchange))
|
||||
|
||||
def test_ezone(self):
|
||||
# Changes from example in spec: Changed CNT from 27 to 39, because we only generate that
|
||||
# and it makes no difference which one we use
|
||||
# Removed DTM for transaction, HSBC ignores it (section 2.8.3)
|
||||
|
||||
expected = """UNB+UNOA:3+::ABC12016001+::HEXAGON ABC+080110:0856+EZONE'
|
||||
UNH+1+PAYMUL:D:96A:UN:FUN01G'
|
||||
BGM+452+EZONE+9'
|
||||
DTM+137:20080110:102'
|
||||
LIN+1'
|
||||
DTM+203:20080114:102'
|
||||
RFF+AEK:EZONE'
|
||||
MOA+9:1.00:EUR'
|
||||
FII+OR+12345678:ACCOUNT HOLDER NAME::EUR+:::403124:154:133+GB'
|
||||
NAD+OY++ORD PARTY NAME NADOY 01:CRG TC5 001 NADOY ADDRESS LINE 0001:CRG TC5 001 NADOY ADDRESS LINE 0002'
|
||||
SEQ++1'
|
||||
MOA+9:1.00:EUR'
|
||||
RFF+CR:EZONE 1A'
|
||||
RFF+PQ:EZONE 1A'
|
||||
PAI+::2'
|
||||
FCA+14'
|
||||
FII+BF+DE23300308800099990031:CRG TC5 001 BENE NAME FIIBF 000001::EUR+AACSDE33:25:5:::+DE'
|
||||
NAD+BE+++BENE NAME NADBE T1 001:CRG TC5 001T1 NADBE ADD LINE 1 0001:CRG TC5 001T1 NADBE ADD LINE 2 0001'
|
||||
CNT+39:1'
|
||||
UNT+19+1'
|
||||
UNZ+1+EZONE'"""
|
||||
|
||||
|
||||
src_account = paymul.UKAccount(number=12345678,
|
||||
holder='ACCOUNT HOLDER NAME',
|
||||
currency='EUR',
|
||||
sortcode=403124)
|
||||
|
||||
dest_account = paymul.IBANAccount(iban="DE23300308800099990031",
|
||||
holder="CRG TC5 001 BENE NAME FIIBF 000001",
|
||||
currency='EUR',
|
||||
bic="AACSDE33")
|
||||
|
||||
party_name = "BENE NAME NADBE T1 001\n" \
|
||||
+ "CRG TC5 001T1 NADBE ADD LINE 1 0001\n" \
|
||||
+ "CRG TC5 001T1 NADBE ADD LINE 2 0001"
|
||||
transaction = paymul.Transaction(amount=Decimal('1.00'),
|
||||
currency='EUR',
|
||||
account=dest_account,
|
||||
party_name=party_name,
|
||||
charges=paymul.CHARGES_EACH_OWN,
|
||||
means=paymul.MEANS_EZONE,
|
||||
customer_reference='EZONE 1A',
|
||||
payment_reference='EZONE 1A')
|
||||
|
||||
name_address = "ORD PARTY NAME NADOY 01\n" \
|
||||
+ "CRG TC5 001 NADOY ADDRESS LINE 0001\n" \
|
||||
+ "CRG TC5 001 NADOY ADDRESS LINE 0002"
|
||||
batch = paymul.Batch(exec_date=datetime.date(2008, 1, 14),
|
||||
reference='EZONE',
|
||||
debit_account=src_account,
|
||||
name_address=name_address)
|
||||
batch.transactions.append(transaction)
|
||||
|
||||
message = paymul.Message(reference='EZONE',
|
||||
dt=datetime.datetime(2008, 1, 10))
|
||||
message.batches.append(batch)
|
||||
|
||||
interchange = paymul.Interchange(client_id='ABC12016001',
|
||||
reference='EZONE',
|
||||
create_dt=datetime.datetime(2008, 1, 10, 8, 56),
|
||||
message=message)
|
||||
|
||||
self.assertMultiLineEqual(expected, str(interchange))
|
||||
|
||||
def test_uk_low_value_ach_instruction_level(self):
|
||||
dest_account1 = paymul.UKAccount(number=87654321,
|
||||
holder="HSBC NET RPS TEST\nHSBC BANK",
|
||||
currency='GBP',
|
||||
sortcode=403124)
|
||||
name_address = "HSBC BANK PLC\n" \
|
||||
+ "PCM\n" \
|
||||
+ "8CS37\n" \
|
||||
+ "E14 5HQ\n" \
|
||||
+ "UNITED KINGDOM"
|
||||
transaction1 = paymul.Transaction(amount=Decimal('1.00'),
|
||||
currency='GBP',
|
||||
account=dest_account1,
|
||||
name_address=name_address,
|
||||
charges=paymul.CHARGES_PAYEE,
|
||||
means=paymul.MEANS_ACH,
|
||||
customer_reference='CREDIT',
|
||||
payment_reference='CREDIT')
|
||||
|
||||
dest_account2 = paymul.UKAccount(number=12341234,
|
||||
holder="HSBC NET RPS TEST\nHSBC BANK",
|
||||
currency='GBP',
|
||||
sortcode=403124)
|
||||
name_address = "HSBC BANK PLC\n" \
|
||||
+ "PCM\n" \
|
||||
+ "8CS37\n" \
|
||||
+ "E14 5HQ\n" \
|
||||
+ "UNITED KINGDOM"
|
||||
transaction2 = paymul.Transaction(amount=Decimal('1.00'),
|
||||
currency='GBP',
|
||||
account=dest_account2,
|
||||
name_address=name_address,
|
||||
charges=paymul.CHARGES_PAYEE,
|
||||
means=paymul.MEANS_ACH,
|
||||
customer_reference='CREDIT1',
|
||||
payment_reference='CREDIT1')
|
||||
|
||||
|
||||
name_address = "HSBC BANK PLC\n" \
|
||||
+ "PCM\n" \
|
||||
+ "8CS37\n" \
|
||||
+ "E14 5HQ\n" \
|
||||
+ "UNITED KINGDOM"
|
||||
|
||||
src_account = paymul.UKAccount(number=12345678,
|
||||
holder='BHEX RPS TEST',
|
||||
currency='GBP',
|
||||
sortcode=401234)
|
||||
batch = paymul.Batch(exec_date=datetime.date(2004, 11, 15),
|
||||
reference='UKLVPLIL',
|
||||
debit_account=src_account,
|
||||
name_address=name_address)
|
||||
batch.transactions = [transaction1, transaction2]
|
||||
|
||||
|
||||
message = paymul.Message(reference='UKLVPLIL',
|
||||
dt=datetime.datetime(2004, 11, 11))
|
||||
message.batches.append(batch)
|
||||
|
||||
|
||||
interchange = paymul.Interchange(client_id='ABC00000001',
|
||||
reference='UKLVPLIL',
|
||||
create_dt=datetime.datetime(2004, 11, 11, 15, 0),
|
||||
message=message)
|
||||
|
||||
|
||||
# Changes from example:
|
||||
# * Change second transaction from EUR to GBP, because we don't support
|
||||
# multi-currency batches
|
||||
# * Removed DTM for transaction, HSBC ignores it (section 2.8.3)
|
||||
expected = """UNB+UNOA:3+::ABC00000001+::HEXAGON ABC+041111:1500+UKLVPLIL'
|
||||
UNH+1+PAYMUL:D:96A:UN:FUN01G'
|
||||
BGM+452+UKLVPLIL+9'
|
||||
DTM+137:20041111:102'
|
||||
LIN+1'
|
||||
DTM+203:20041115:102'
|
||||
RFF+AEK:UKLVPLIL'
|
||||
MOA+9:2.00:GBP'
|
||||
FII+OR+12345678:BHEX RPS TEST::GBP+:::401234:154:133+GB'
|
||||
NAD+OY++HSBC BANK PLC:PCM:8CS37:E14 5HQ:UNITED KINGDOM'
|
||||
SEQ++1'
|
||||
MOA+9:1.00:GBP'
|
||||
RFF+CR:CREDIT'
|
||||
RFF+PQ:CREDIT'
|
||||
PAI+::2'
|
||||
FCA+13'
|
||||
FII+BF+87654321:HSBC NET RPS TEST:HSBC BANK:GBP+:::403124:154:133+GB'
|
||||
NAD+BE++HSBC BANK PLC:PCM:8CS37:E14 5HQ:UNITED KINGDOM'
|
||||
SEQ++2'
|
||||
MOA+9:1.00:GBP'
|
||||
RFF+CR:CREDIT1'
|
||||
RFF+PQ:CREDIT1'
|
||||
PAI+::2'
|
||||
FCA+13'
|
||||
FII+BF+12341234:HSBC NET RPS TEST:HSBC BANK:GBP+:::403124:154:133+GB'
|
||||
NAD+BE++HSBC BANK PLC:PCM:8CS37:E14 5HQ:UNITED KINGDOM'
|
||||
CNT+39:2'
|
||||
UNT+27+1'
|
||||
UNZ+1+UKLVPLIL'"""
|
||||
|
||||
self.assertMultiLineEqual(expected, str(interchange))
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# I ran this with
|
||||
# env PYTHONPATH=$HOME/src/canonical/hsbc-banking:$HOME/src/openerp/6.0/server/bin:$HOME/src/openerp/6.0/addons python wizard/paymul_test.py
|
||||
# is there a better way?
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user