From 3a3151c62d16e6ca9bd1f57f122be962f768f705 Mon Sep 17 00:00:00 2001 From: Tristan Hill Date: Mon, 24 Oct 2011 16:26:13 +0100 Subject: [PATCH 01/14] [IMP] import module to integrate with HSBC --- account_banking_uk_hsbc/__init__.py | 24 + account_banking_uk_hsbc/__openerp__.py | 45 ++ .../account_banking_uk_hsbc.py | 65 +++ .../account_banking_uk_hsbc.xml | 85 ++++ .../data/banking_export_hsbc.xml | 13 + account_banking_uk_hsbc/wizard/__init__.py | 23 + account_banking_uk_hsbc/wizard/export_hsbc.py | 339 +++++++++++++ .../wizard/export_hsbc_view.xml | 37 ++ account_banking_uk_hsbc/wizard/paymul.py | 473 ++++++++++++++++++ account_banking_uk_hsbc/wizard/paymul_test.py | 274 ++++++++++ 10 files changed, 1378 insertions(+) create mode 100644 account_banking_uk_hsbc/__init__.py create mode 100644 account_banking_uk_hsbc/__openerp__.py create mode 100644 account_banking_uk_hsbc/account_banking_uk_hsbc.py create mode 100644 account_banking_uk_hsbc/account_banking_uk_hsbc.xml create mode 100644 account_banking_uk_hsbc/data/banking_export_hsbc.xml create mode 100644 account_banking_uk_hsbc/wizard/__init__.py create mode 100644 account_banking_uk_hsbc/wizard/export_hsbc.py create mode 100644 account_banking_uk_hsbc/wizard/export_hsbc_view.xml create mode 100644 account_banking_uk_hsbc/wizard/paymul.py create mode 100644 account_banking_uk_hsbc/wizard/paymul_test.py diff --git a/account_banking_uk_hsbc/__init__.py b/account_banking_uk_hsbc/__init__.py new file mode 100644 index 000000000..2ecd93336 --- /dev/null +++ b/account_banking_uk_hsbc/__init__.py @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2011 credativ Ltd (). +# 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 . +# +############################################################################## + +import account_banking_uk_hsbc +import wizard +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/account_banking_uk_hsbc/__openerp__.py b/account_banking_uk_hsbc/__openerp__.py new file mode 100644 index 000000000..6f2e6a60c --- /dev/null +++ b/account_banking_uk_hsbc/__openerp__.py @@ -0,0 +1,45 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2011 credativ Ltd (). +# 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 . +# +############################################################################## +{ + '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, +} diff --git a/account_banking_uk_hsbc/account_banking_uk_hsbc.py b/account_banking_uk_hsbc/account_banking_uk_hsbc.py new file mode 100644 index 000000000..77bc7b09b --- /dev/null +++ b/account_banking_uk_hsbc/account_banking_uk_hsbc.py @@ -0,0 +1,65 @@ +############################################################################## +# +# Copyright (C) 2009 EduSense BV (). +# Copyright (C) 2011 credativ Ltd (). +# 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 . +# +############################################################################## + +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: diff --git a/account_banking_uk_hsbc/account_banking_uk_hsbc.xml b/account_banking_uk_hsbc/account_banking_uk_hsbc.xml new file mode 100644 index 000000000..02b382256 --- /dev/null +++ b/account_banking_uk_hsbc/account_banking_uk_hsbc.xml @@ -0,0 +1,85 @@ + + + + + + + + account.banking.export.hsbc.form + banking.export.hsbc + form + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + account.banking.export.hsbc.tree + banking.export.hsbc + tree + + + + + + + + + Generated HSBC files + ir.actions.act_window + banking.export.hsbc + form + tree,form + + + + + + + + +
+
diff --git a/account_banking_uk_hsbc/data/banking_export_hsbc.xml b/account_banking_uk_hsbc/data/banking_export_hsbc.xml new file mode 100644 index 000000000..0b2e75e1b --- /dev/null +++ b/account_banking_uk_hsbc/data/banking_export_hsbc.xml @@ -0,0 +1,13 @@ + + + + + HSBC + HSBC + + + + + diff --git a/account_banking_uk_hsbc/wizard/__init__.py b/account_banking_uk_hsbc/wizard/__init__.py new file mode 100644 index 000000000..83ee32b16 --- /dev/null +++ b/account_banking_uk_hsbc/wizard/__init__.py @@ -0,0 +1,23 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2009 EduSense BV (). +# Copyright (C) 2011 credativ Ltd (). +# 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 . +# +############################################################################## + +import export_hsbc \ No newline at end of file diff --git a/account_banking_uk_hsbc/wizard/export_hsbc.py b/account_banking_uk_hsbc/wizard/export_hsbc.py new file mode 100644 index 000000000..44f2f7bc7 --- /dev/null +++ b/account_banking_uk_hsbc/wizard/export_hsbc.py @@ -0,0 +1,339 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2009 EduSense BV (). +# Copyright (C) 2011 credativ Ltd (). +# 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 . +# +############################################################################## + +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: diff --git a/account_banking_uk_hsbc/wizard/export_hsbc_view.xml b/account_banking_uk_hsbc/wizard/export_hsbc_view.xml new file mode 100644 index 000000000..617080c61 --- /dev/null +++ b/account_banking_uk_hsbc/wizard/export_hsbc_view.xml @@ -0,0 +1,37 @@ + + + + + banking.export.hsbc.wizard.view + banking.export.hsbc.wizard + form + +
+ + + + + + + + + +