From 0c2aec9eeb243dbd822470dbf7f31c0080b44312 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Thu, 24 Sep 2015 11:38:50 +0200 Subject: [PATCH] Add a hook to inherit grouping of the transfer account move line Use that new hook in SEPA direct debits Better variable names --- .../model/__init__.py | 3 + .../model/account_payment.py | 59 +++++------ .../model/bank_payment_line.py | 97 +++++++++++++++++++ .../model/payment_line.py | 69 ++----------- .../models/__init__.py | 23 +---- .../models/bank_payment_line.py | 41 ++++++++ 6 files changed, 185 insertions(+), 107 deletions(-) create mode 100644 account_banking_payment_transfer/model/bank_payment_line.py create mode 100644 account_banking_sepa_direct_debit/models/bank_payment_line.py diff --git a/account_banking_payment_transfer/model/__init__.py b/account_banking_payment_transfer/model/__init__.py index 7a8cfda04..09e695000 100644 --- a/account_banking_payment_transfer/model/__init__.py +++ b/account_banking_payment_transfer/model/__init__.py @@ -1,4 +1,7 @@ +# -*- coding: utf-8 -*- + from . import account_payment from . import payment_line +from . import bank_payment_line from . import payment_mode from . import account_move_reconcile diff --git a/account_banking_payment_transfer/model/account_payment.py b/account_banking_payment_transfer/model/account_payment.py index f79308a56..2fceef2ac 100644 --- a/account_banking_payment_transfer/model/account_payment.py +++ b/account_banking_payment_transfer/model/account_payment.py @@ -4,7 +4,7 @@ # Copyright (C) 2009 EduSense BV (). # (C) 2011 - 2013 Therp BV (). # (C) 2014 ACSONE SA (). -# (C) 2014 Akretion (www.akretion.com) +# (C) 2014-2015 Akretion (www.akretion.com) # # All other contributions are (C) by their respective contributors # @@ -26,6 +26,7 @@ ############################################################################## from openerp import models, fields, api, _ +from openerp.exceptions import Warning as UserError class PaymentOrder(models.Model): @@ -165,23 +166,23 @@ class PaymentOrder(models.Model): return vals @api.multi - def _prepare_move_line_partner_account(self, line, move, labels): + def _prepare_move_line_partner_account(self, bank_line, move, labels): # TODO : ALEXIS check don't group if move_line_id.account_id # is not the same if self.payment_order_type == 'debit': - account_id = line.partner_id.property_account_receivable.id + account_id = bank_line.partner_id.property_account_receivable.id else: - account_id = line.partner_id.property_account_payable.id + account_id = bank_line.partner_id.property_account_payable.id vals = { 'name': _('%s line %s') % ( - labels[self.payment_order_type], line.name), + labels[self.payment_order_type], bank_line.name), 'move_id': move.id, - 'partner_id': line.partner_id.id, + 'partner_id': bank_line.partner_id.id, 'account_id': account_id, 'credit': (self.payment_order_type == 'debit' and - line.amount_currency or 0.0), + bank_line.amount_currency or 0.0), 'debit': (self.payment_order_type == 'payment' and - line.amount_currency or 0.0), + bank_line.amount_currency or 0.0), } return vals @@ -191,20 +192,27 @@ class PaymentOrder(models.Model): return @api.multi - def _create_move_line_partner_account(self, line, move, labels): + def _create_move_line_partner_account(self, bank_line, move, labels): """This method is designed to be inherited in a custom module""" - # TODO: take multicurrency into account + company_currency = self.env.user.company_id.currency_id + if bank_line.currency != company_currency: + raise UserError(_( + "Cannot generate the transfer move when " + "the currency of the payment (%s) is not the " + "same as the currency of the company. This " + "is not supported for the moment.") + % (bank_line.currency.name, company_currency.name)) aml_obj = self.env['account.move.line'] # create the payment/debit counterpart move line # on the partner account partner_ml_vals = self._prepare_move_line_partner_account( - line, move, labels) + bank_line, move, labels) partner_move_line = aml_obj.create(partner_ml_vals) # register the payment/debit move line # on the payment line and call reconciliation on it - line.write({'transit_move_line_id': partner_move_line.id}) + bank_line.write({'transit_move_line_id': partner_move_line.id}) @api.multi def _reconcile_payment_lines(self, bank_payment_lines): @@ -233,28 +241,25 @@ class PaymentOrder(models.Model): # key = unique identifier (date or True or line.id) # value = [pay_line1, pay_line2, ...] trfmoves = {} - if self.mode.transfer_move_option == 'line': - for line in self.bank_line_ids: - trfmoves[line.id] = [line] - else: - for line in self.bank_line_ids: - if line.date in trfmoves: - trfmoves[line.date].append(line) - else: - trfmoves[line.date] = [line] + for bline in self.bank_line_ids: + hashcode = bline.move_line_transfer_account_hashcode() + if hashcode in trfmoves: + trfmoves[hashcode].append(bline) + else: + trfmoves[hashcode] = [bline] - for identifier, lines in trfmoves.iteritems(): + for hashcode, blines in trfmoves.iteritems(): mvals = self._prepare_transfer_move() move = am_obj.create(mvals) total_amount = 0 - for line in lines: - total_amount += line.amount_currency - self._create_move_line_partner_account(line, move, labels) + for bline in blines: + total_amount += bline.amount_currency + self._create_move_line_partner_account(bline, move, labels) # create the payment/debit move line on the transfer account trf_ml_vals = self._prepare_move_line_transfer_account( - total_amount, move, lines, labels) + total_amount, move, blines, labels) aml_obj.create(trf_ml_vals) - self._reconcile_payment_lines(lines) + self._reconcile_payment_lines(blines) # consider entry_posted on account_journal if move.journal_id.entry_posted: diff --git a/account_banking_payment_transfer/model/bank_payment_line.py b/account_banking_payment_transfer/model/bank_payment_line.py new file mode 100644 index 000000000..d1b60360c --- /dev/null +++ b/account_banking_payment_transfer/model/bank_payment_line.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2009 EduSense BV (). +# (C) 2011 - 2013 Therp BV (). +# (C) 2015 Akretion (http://www.akretion.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 . +# +############################################################################## + +from openerp import models, fields, api + + +class BankPaymentLine(models.Model): + _inherit = 'bank.payment.line' + + transit_move_line_id = fields.Many2one( + 'account.move.line', string='Transfer move line', readonly=True, + help="Move line through which the payment/debit order " + "pays the invoice") + transfer_move_line_id = fields.Many2one( + 'account.move.line', compute='_get_transfer_move_line', + string='Transfer move line counterpart', + help="Counterpart move line on the transfer account") + + @api.multi + def move_line_transfer_account_hashcode(self): + """ + This method is inherited in the module + account_banking_sepa_direct_debit + """ + self.ensure_one() + if self.order_id.mode.transfer_move_option == 'date': + hashcode = self.date + else: + hashcode = unicode(self.id) + return hashcode + + @api.multi + def _get_transfer_move_line(self): + for bank_line in self: + if bank_line.transit_move_line_id: + order_type = bank_line.order_id.payment_order_type + trf_lines = bank_line.transit_move_line_id.move_id.line_id + for move_line in trf_lines: + if order_type == 'debit' and move_line.debit > 0: + bank_line.transfer_move_line_id = move_line + elif order_type == 'payment' and move_line.credit > 0: + bank_line.transfer_move_line_id = move_line + + @api.one + def debit_reconcile(self): + """ + Reconcile a debit order's payment line with the the move line + that it is based on. Called from payment_order.action_sent(). + As the amount is derived directly from the counterpart move line, + we do not expect a write off. Take partial reconciliations into + account though. + + :param payment_line_id: the single id of the canceled payment line + """ + + transit_move_line = self.transit_move_line_id + +# if (not transit_move_line or not torec_move_line): +# raise exceptions.Warning( +# _('Can not reconcile: no move line for line %s') % self.name +# ) +# if torec_move_line.reconcile_id: +# raise exceptions.Warning( +# _('Move line %s has already been reconciled') % +# torec_move_line.name +# ) +# if (transit_move_line.reconcile_id or +# transit_move_line.reconcile_partial_id): +# raise exceptions.Warning( +# _('Move line %s has already been reconciled') % +# transit_move_line.name +# ) + + lines_to_rec = transit_move_line + for payment_line in self.payment_line_ids: + lines_to_rec += payment_line.move_line_id + + lines_to_rec.reconcile_partial(type='auto') diff --git a/account_banking_payment_transfer/model/payment_line.py b/account_banking_payment_transfer/model/payment_line.py index 76262a4b3..ee0be94b3 100644 --- a/account_banking_payment_transfer/model/payment_line.py +++ b/account_banking_payment_transfer/model/payment_line.py @@ -22,8 +22,8 @@ # along with this program. If not, see . # ############################################################################## -from openerp import models, fields, workflow, api, exceptions -from openerp.tools.translate import _ + +from openerp import models, fields, api class PaymentLine(models.Model): @@ -38,63 +38,14 @@ class PaymentLine(models.Model): msg = fields.Char('Message', required=False, readonly=True, default='') date_done = fields.Date('Date Confirmed', select=True, readonly=True) - -class BankPaymentLine(models.Model): - _inherit = 'bank.payment.line' - - transit_move_line_id = fields.Many2one( - 'account.move.line', string='Transfer move line', readonly=True, - help="Move line through which the payment/debit order " - "pays the invoice") - transfer_move_line_id = fields.Many2one( - 'account.move.line', compute='_get_transfer_move_line', - string='Transfer move line counterpart', - help="Counterpart move line on the transfer account") - @api.multi - def _get_transfer_move_line(self): - for bank_line in self: - if bank_line.transit_move_line_id: - order_type = bank_line.order_id.payment_order_type - trf_lines = bank_line.transit_move_line_id.move_id.line_id - for move_line in trf_lines: - if order_type == 'debit' and move_line.debit > 0: - bank_line.transfer_move_line_id = move_line - elif order_type == 'payment' and move_line.credit > 0: - bank_line.transfer_move_line_id = move_line - - @api.one - def debit_reconcile(self): + def payment_line_hashcode(self): """ - Reconcile a debit order's payment line with the the move line - that it is based on. Called from payment_order.action_sent(). - As the amount is derived directly from the counterpart move line, - we do not expect a write off. Take partial reconciliations into - account though. - - :param payment_line_id: the single id of the canceled payment line + Don't group the payment lines that are attached to the same supplier + but to move lines with different accounts (very unlikely), + for easier generation/comprehension of the transfer move """ - - transit_move_line = self.transit_move_line_id - -# if (not transit_move_line or not torec_move_line): -# raise exceptions.UserError( -# _('Can not reconcile: no move line for line %s') % self.name -# ) -# if torec_move_line.reconcile_id: -# raise exceptions.UserError( -# _('Move line %s has already been reconciled') % -# torec_move_line.name -# ) -# if (transit_move_line.reconcile_id or -# transit_move_line.reconcile_partial_id): -# raise exceptions.UserError( -# _('Move line %s has already been reconciled') % -# transit_move_line.name -# ) - - lines_to_rec = transit_move_line - for payment_line in self.payment_line_ids: - lines_to_rec += payment_line.move_line_id - - lines_to_rec.reconcile_partial(type='auto') + res = super(PaymentLine, self).payment_line_hashcode() + res += '-' + unicode( + self.move_line_id and self.move_line_id.account_id or False) + return res diff --git a/account_banking_sepa_direct_debit/models/__init__.py b/account_banking_sepa_direct_debit/models/__init__.py index 153ab3543..ac7674156 100644 --- a/account_banking_sepa_direct_debit/models/__init__.py +++ b/account_banking_sepa_direct_debit/models/__init__.py @@ -1,24 +1,5 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# SEPA Direct Debit module for OpenERP -# Copyright (C) 2013 Akretion (http://www.akretion.com) -# @author: Alexis de Lattre -# -# 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 . -# -############################################################################## +# -*- coding: utf-8 -*- from . import res_company from . import account_banking_mandate +from . import bank_payment_line diff --git a/account_banking_sepa_direct_debit/models/bank_payment_line.py b/account_banking_sepa_direct_debit/models/bank_payment_line.py new file mode 100644 index 000000000..9da270e80 --- /dev/null +++ b/account_banking_sepa_direct_debit/models/bank_payment_line.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# SEPA Direct Debit module for Odoo +# Copyright (C) 2015 Akretion (http://www.akretion.com) +# @author: Alexis de Lattre +# +# 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 . +# +############################################################################## + +from openerp import models, api + + +class BankPaymentLine(models.Model): + _inherit = 'bank.payment.line' + + @api.multi + def move_line_transfer_account_hashcode(self): + """ + From my experience, even when you ask several direct debits + at the same date with enough delay, you will have several credits + on your bank statement: one for each mandate types. + So we split the transfer move lines by mandate type, so easier + reconciliation of the bank statement. + """ + hashcode = super(BankPaymentLine, self).\ + move_line_transfer_account_hashcode() + hashcode += self.mandate_id.type + return hashcode