From 5ae5d657f8fedf4a008c1283389112a56191ebf6 Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Mon, 27 May 2013 21:36:20 +0200 Subject: [PATCH] [ADD] Foreporting crude support for full payment workflow including transferral move that pays the invoices --- account_banking/banking_import_transaction.py | 20 +++++--- account_banking_nl_girotel/girotel.py | 16 +++++- .../model/banking_import_transaction.py | 50 ++++++++++++------- account_direct_debit/model/account_payment.py | 27 ++++++---- 4 files changed, 74 insertions(+), 39 deletions(-) diff --git a/account_banking/banking_import_transaction.py b/account_banking/banking_import_transaction.py index 0e223b84f..14a38f0e3 100644 --- a/account_banking/banking_import_transaction.py +++ b/account_banking/banking_import_transaction.py @@ -810,6 +810,12 @@ class banking_import_transaction(orm.Model): move_info['invoice_ids'][0] ) return vals + + def hook_match_payment(self, cr, uid, transaction, log, context=None): + """ + To override in module 'account_banking_payment' + """ + return False def match(self, cr, uid, ids, results=None, context=None): if not ids: @@ -1020,13 +1026,13 @@ class banking_import_transaction(orm.Model): ), context=context) # rebrowse the current record after writing transaction = self.browse(cr, uid, transaction.id, context=context) - # Match full direct debit orders - if transaction.type == bt.DIRECT_DEBIT and has_payment: - move_info = self._match_debit_order( - cr, uid, transaction, results['log'], context) - if transaction.type == bt.STORNO and has_payment: - move_info = self._match_storno( - cr, uid, transaction, results['log'], context) + + # Match payment and direct debit orders + move_info_payment = hook_match_payment( + cr, uid, transaction, results['log'], context=context) + if move_info_payment: + move_info = move_info_payment + # Allow inclusion of generated bank invoices if transaction.type == bt.BANK_COSTS: lines = self._match_costs( diff --git a/account_banking_nl_girotel/girotel.py b/account_banking_nl_girotel/girotel.py index 9b0e56087..792770f19 100644 --- a/account_banking_nl_girotel/girotel.py +++ b/account_banking_nl_girotel/girotel.py @@ -45,6 +45,7 @@ As a counter measure, all imported data is converted to SWIFT-format before usag from account_banking.parsers import models from account_banking.parsers.convert import str2date, to_swift from tools.translate import _ +import re import csv bt = models.mem_bank_transaction @@ -105,6 +106,13 @@ class transaction_message(object): self.date = str2date(self.date, '%Y%m%d') if self.direction == 'A': self.transferred_amount = -float(self.transferred_amount) + if (self.transfer_type == 'VZ' + and (not self.remote_account or self.remote_account == '0') + and (not self.message or re.match('^\s*$', self.message)) + and self.remote_owner.startswith('TOTAAL ')): + self.transfer_type = 'PB' + self.message = self.remote_owner + self.remove_owner = False else: self.transferred_amount = float(self.transferred_amount) self.local_account = self.local_account.zfill(10) @@ -140,7 +148,8 @@ class transaction(models.mem_bank_transaction): 'GT': bt.ORDER, 'IC': bt.DIRECT_DEBIT, 'OV': bt.ORDER, - 'VZ': bt.PAYMENT_BATCH, + 'VZ': bt.ORDER, + 'PB': bt.PAYMENT_BATCH, } def __init__(self, line, *args, **kwargs): @@ -171,11 +180,14 @@ class transaction(models.mem_bank_transaction): 4. Cash withdrawals from banks are too not seen as a transfer between two accounts - the cash exits the banking system. These withdrawals have their transfer_type set to 'GM'. + 5. Aggregated payment batches. These transactions have transfer type + 'VZ' natively but are changed to 'PB' while parsing. These transactions + have no remote account. ''' return bool(self.transferred_amount and self.execution_date and ( self.remote_account or self.transfer_type in [ - 'DV', 'BT', 'BA', 'GM', + 'DV', 'PB', 'BT', 'BA', 'GM', ])) def refold_message(self, message): diff --git a/account_banking_payment/model/banking_import_transaction.py b/account_banking_payment/model/banking_import_transaction.py index 21cfc2fd4..e08a61ffb 100644 --- a/account_banking_payment/model/banking_import_transaction.py +++ b/account_banking_payment/model/banking_import_transaction.py @@ -32,24 +32,30 @@ from openerp.addons.decimal_precision import decimal_precision as dp class banking_import_transaction(orm.Model): _inherit = 'banking.import.transaction' - def _match_debit_order( - self, cr, uid, trans, log, context=None): + def _match_payment_order( + self, cr, uid, trans, log, order_type='payment', context=None): - def is_zero(total): + def equals_order_amount(payment_order, transferred_amount): + if (not hasattr(payment_order, 'payment_order_type') + or payment_order.payment_order_type == 'payment'): + sign = 1 + else: + sign = -1 + total = payment_order.total + sign * transferred_amount return self.pool.get('res.currency').is_zero( cr, uid, trans.statement_id.currency, total) payment_order_obj = self.pool.get('payment.order') order_ids = payment_order_obj.search( - cr, uid, [('payment_order_type', '=', 'debit'), + cr, uid, [('payment_order_type', '=', order_type), ('state', '=', 'sent'), ('date_sent', '<=', trans.execution_date), ], limit=0, context=context) orders = payment_order_obj.browse(cr, uid, order_ids, context) candidates = [x for x in orders if - is_zero(x.total - trans.transferred_amount)] + equals_order_amount(x.total - trans.transferred_amount)] if len(candidates) > 0: # retrieve the common account_id, if any account_id = False @@ -170,10 +176,6 @@ class banking_import_transaction(orm.Model): raise orm.except_orm( _("Cannot reconcile"), _("Cannot reconcile: no direct debit order")) - if transaction.payment_order_id.payment_order_type != 'debit': - raise orm.except_orm( - _("Cannot reconcile"), - _("Reconcile payment order not implemented")) reconcile_id = payment_order_obj.debit_reconcile_transfer( cr, uid, transaction.payment_order_id.id, @@ -231,11 +233,7 @@ class banking_import_transaction(orm.Model): if not transaction.payment_order_id: raise orm.except_orm( _("Cannot unreconcile"), - _("Cannot unreconcile: no direct debit order")) - if transaction.payment_order_id.payment_order_type != 'debit': - raise orm.except_orm( - _("Cannot unreconcile"), - _("Unreconcile payment order not implemented")) + _("Cannot unreconcile: no payment or direct debit order")) return payment_order_obj.debit_unreconcile_transfer( cr, uid, transaction.payment_order_id.id, transaction.statement_line_id.reconcile_id.id, @@ -355,11 +353,25 @@ class banking_import_transaction(orm.Model): ) return vals - def match(self, cr, uid, ids, results=None, context=None): - res = super(banking_import_transaction, self).match( - cr, uid, ids, results=results, context=context) - - return res + def hook_match_payment(cr, uid, transaction, log, context=None): + """ + Called from match() in the core module. + Match payment batches, direct debit orders and stornos + """ + move_info = False + if transaction.type == bt.PAYMENT_BATCH: + move_info = self._match_payment_order( + cr, uid, transaction, log, + order_type='payment', context=context) + elif transaction.type == bt.DIRECT_DEBIT: + move_info = self._match_payment_order( + cr, uid, transaction, log, + order_type='debit', context=context) + elif transaction.type == bt.STORNO: + move_info = self._match_storno( + cr, uid, transaction, log, + context=context) + return move_info def __init__(self, pool, cr): """ diff --git a/account_direct_debit/model/account_payment.py b/account_direct_debit/model/account_payment.py index fe4cb6d7b..56c9fcbbf 100644 --- a/account_direct_debit/model/account_payment.py +++ b/account_direct_debit/model/account_payment.py @@ -102,7 +102,7 @@ class payment_order(osv.osv): if not wkf_ok: raise osv.except_osv( _("Cannot unreconcile"), - _("Cannot unreconcile debit order: "+ + _("Cannot unreconcile payment order: "+ "Workflow will not allow it.")) return True @@ -119,7 +119,7 @@ class payment_order(osv.osv): return False else: # TODO: define conditions for 'payment' orders - return False + return True return True def action_sent(self, cr, uid, ids, context=None): @@ -135,8 +135,6 @@ class payment_order(osv.osv): account_move_line_obj = self.pool.get('account.move.line') payment_line_obj = self.pool.get('payment.line') for order in self.browse(cr, uid, ids, context=context): - if order.payment_order_type != 'debit': - continue for line in order.line_ids: # basic checks if not line.move_line_id: @@ -152,23 +150,28 @@ class payment_order(osv.osv): move_id = account_move_obj.create(cr, uid, { 'journal_id': order.mode.transfer_journal_id.id, - 'name': 'Debit order %s' % line.move_line_id.move_id.name, - 'reference': 'DEB%s' % line.move_line_id.move_id.name, + 'name': '%s order %s' % (order.payment_order_type, + line.move_line_id.move_id.name), + 'reference': '%s%s' % (order.payment_order_type[:3].upper(), + line.move_line_id.move_id.name), }, context=context) # TODO: take multicurrency into account # create the debit move line on the transfer account vals = { - 'name': 'Debit order for %s' % ( + 'name': '%s order for %s' % ( + order.payment_order_type, line.move_line_id.invoice and line.move_line_id.invoice.number or line.move_line_id.name), 'move_id': move_id, 'partner_id': line.partner_id.id, 'account_id': order.mode.transfer_account_id.id, - 'credit': 0.0, - 'debit': line.amount, + 'credit': (order.payment_order_type == 'payment' + and line.amount or 0.0), + 'debit': (order.payment_order_type == 'debit' + and line.amount or 0.0), 'date': time.strftime('%Y-%m-%d'), } transfer_move_line_id = account_move_line_obj.create( @@ -177,8 +180,10 @@ class payment_order(osv.osv): # create the debit move line on the receivable account vals.update({ 'account_id': line.move_line_id.account_id.id, - 'credit': line.amount, - 'debit': 0.0, + 'credit': (order.payment_order_type == 'debit' + and line.amount or 0.0), + 'debit': (order.payment_order_type == 'payment' + and line.amount or 0.0), }) reconcile_move_line_id = account_move_line_obj.create( cr, uid, vals, context=context)