1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
6
# All other contributions are (C) by their respective contributors
10
# This program is free software: you can redistribute it and/or modify
11
# it under the terms of the GNU Affero General Public License as
12
# published by the Free Software Foundation, either version 3 of the
13
# License, or (at your option) any later version.
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
# GNU Affero General Public License for more details.
20
# You should have received a copy of the GNU Affero General Public License
21
# along with this program. If not, see <http://www.gnu.org/licenses/>.
23
##############################################################################
25
from openerp.osv import orm
26
from openerp.tools.translate import _
29
This module adds support for Direct debit orders as applicable
30
in the Netherlands. Debit orders are advanced in total by the bank.
31
Amounts that cannot be debited or are canceled by account owners are
32
credited afterwards. Such a creditation is called a storno.
44
2 an external booking takes place
47
This booking is reconciled with [1]
48
The invoice gets set to state 'paid', and 'reconciled' = True
55
This module implements the following diversion:
57
2a the invoice is included in a direct debit order. When the order is
58
confirmed, a move is created per invoice:
60
2000 Transfer account 100 |
62
Reconciliation takes place between 1 and 2a.
63
The invoice gets set to state 'paid', and 'reconciled' = True
67
Transfer account 2000 |
71
3a the direct debit order is booked on the bank account
75
2000 Transfer account | 2000
76
Reconciliation takes place between 3a and 2a
84
4 a storno from invoice [1] triggers a new booking on the bank account
94
The reconciliation of 2a is undone. The booking of 2a is reconciled
95
with the booking of 4 instead.
96
The payment line attribute 'storno' is set to True and the invoice
97
state is no longer 'paid'.
99
Two cases need to be distinguisted:
100
1) If the storno is a manual storno from the partner, the invoice is set to
101
state 'debit_denied', with 'reconciled' = False
102
This module implements this option by allowing the bank module to call
104
netsvc.LocalService("workflow").trg_validate(
105
uid, 'account.invoice', ids, 'debit_denied', cr)
107
2) If the storno is an error generated by the bank (assumingly non-fatal),
108
the invoice is reopened for the next debit run. This is a call to existing
110
netsvc.LocalService("workflow").trg_validate(
111
uid, 'account.invoice', ids, 'open_test', cr)
113
Should also be adding a log entry on the invoice for tracing purposes
115
self._log_event(cr, uid, ids, -1.0, 'Debit denied')
117
If not for that funny comment
118
"#TODO: implement messages system" in account/invoice.py
120
Repeating non-fatal fatal errors need to be dealt with manually by checking
121
open invoices with a matured invoice- or due date.
124
class account_invoice(orm.Model):
125
_inherit = "account.invoice"
127
def __init__(self, pool, cr):
129
Adding a state to the hardcoded state list of the inherited
130
model. The alternative is duplicating the field definition
131
in columns but only one module can do that!
133
Maybe apply a similar trick when overriding the buttons' 'states' attributes
134
in the form view, manipulating the xml in fields_view_get().
136
super(account_invoice, self).__init__(pool, cr)
137
invoice_obj = pool.get('account.invoice')
138
invoice_obj._columns['state'].selection.append(
139
('debit_denied', 'Debit denied'))
141
def action_debit_denied(self, cr, uid, ids, context=None):
142
for invoice_id in ids:
143
if self.test_paid(cr, uid, [invoice_id], context):
145
cr, uid, invoice_id, ['number'], context=context)['number']
146
raise orm.except_orm(
148
_('You cannot set invoice \'%s\' to state \'debit denied\', ' +
149
'as it is still reconciled.') % number)
150
self.write(cr, uid, ids, {'state': 'debit_denied'}, context=context)
151
for inv_id, name in self.name_get(cr, uid, ids, context=context):
152
message = _("Invoice '%s': direct debit is denied.") % name
153
self.log(cr, uid, inv_id, message)
156
def test_undo_debit_denied(self, cr, uid, ids, context=None):
158
Called from the workflow. Used to unset paid state on
159
invoices that were paid with bank transfers which are being cancelled
161
for invoice in self.read(cr, uid, ids, ['reconciled'], context):
162
if not invoice['reconciled']: