~pedro-q/account-payment/account-payment-sepa-7.0

« back to all changes in this revision

Viewing changes to account_payment_direct_debit/model/account_invoice.py

  • Committer: Ignacio Ibeas - Acysos S.L.
  • Date: 2014-01-25 13:30:35 UTC
  • Revision ID: ignacio@acysos.com-20140125133035-46kipms7z97u825v
[ADD] SEPA modules ported from Banking Addons to Account Payment Extension

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
#    Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
 
5
#            
 
6
#    All other contributions are (C) by their respective contributors
 
7
#
 
8
#    All Rights Reserved
 
9
#
 
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.
 
14
#
 
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.
 
19
#
 
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/>.
 
22
#
 
23
##############################################################################
 
24
 
 
25
from openerp.osv import orm
 
26
from openerp.tools.translate import _
 
27
 
 
28
"""
 
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.
 
33
 
 
34
Invoice workflow:
 
35
 
 
36
1 the sale leads to 
 
37
   1300 Debtors 100
 
38
   8000 Sales       100
 
39
 
 
40
Balance: 
 
41
  Debtors  2000 | 
 
42
  Sales        | 2000
 
43
 
 
44
2 an external booking takes place
 
45
   1100 Bank    100
 
46
   1300 Debtors     100
 
47
   This booking is reconciled with [1]
 
48
   The invoice gets set to state 'paid', and 'reconciled' = True
 
49
 
 
50
Balance:
 
51
   Debtors 1900 |
 
52
   Bank     100 |
 
53
   Sales        | 2000
 
54
 
 
55
This module implements the following diversion:
 
56
 
 
57
2a the invoice is included in a direct debit order. When the order is
 
58
   confirmed, a move is created per invoice:
 
59
 
 
60
   2000 Transfer account 100 |
 
61
   1300 Debtors              | 100
 
62
   Reconciliation takes place between 1 and 2a. 
 
63
   The invoice gets set to state 'paid', and 'reconciled' = True
 
64
 
 
65
Balance:
 
66
   Debtors             0 |  
 
67
   Transfer account 2000 |
 
68
   Bank                0 |
 
69
   Sales                 | 2000
 
70
 
 
71
3a the direct debit order is booked on the bank account
 
72
 
 
73
Balance:
 
74
  1100 Bank             2000 |
 
75
  2000 Transfer account      | 2000
 
76
  Reconciliation takes place between 3a and 2a
 
77
 
 
78
Balance:
 
79
   Debtors             0 |  
 
80
   Transfer account    0 |
 
81
   Bank             2000 |
 
82
   Sales                 | 2000
 
83
 
 
84
4 a storno from invoice [1] triggers a new booking on the bank account
 
85
  1300 Debtors 100 |
 
86
  1100 Bank        | 100
 
87
  
 
88
Balance:
 
89
   Debtors           100 |  
 
90
   Transfer account    0 |
 
91
   Bank             1900 |
 
92
   Sales                 | 2000
 
93
 
 
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'.
 
98
 
 
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
 
103
     
 
104
          netsvc.LocalService("workflow").trg_validate(
 
105
              uid, 'account.invoice', ids, 'debit_denied', cr)
 
106
 
 
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
 
109
     
 
110
         netsvc.LocalService("workflow").trg_validate(
 
111
             uid, 'account.invoice', ids, 'open_test', cr)
 
112
 
 
113
      Should also be adding a log entry on the invoice for tracing purposes
 
114
 
 
115
         self._log_event(cr, uid, ids, -1.0, 'Debit denied')           
 
116
 
 
117
      If not for that funny comment
 
118
      "#TODO: implement messages system"  in account/invoice.py
 
119
 
 
120
   Repeating non-fatal fatal errors need to be dealt with manually by checking
 
121
   open invoices with a matured invoice- or due date.
 
122
""" 
 
123
 
 
124
class account_invoice(orm.Model):
 
125
    _inherit = "account.invoice"
 
126
 
 
127
    def __init__(self, pool, cr):
 
128
        """ 
 
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!
 
132
 
 
133
        Maybe apply a similar trick when overriding the buttons' 'states' attributes
 
134
        in the form view, manipulating the xml in fields_view_get().
 
135
        """ 
 
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'))
 
140
 
 
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):
 
144
                number = self.read(
 
145
                    cr, uid, invoice_id, ['number'], context=context)['number']
 
146
                raise orm.except_orm(
 
147
                    _('Error !'), 
 
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)
 
154
        return True
 
155
 
 
156
    def test_undo_debit_denied(self, cr, uid, ids, context=None):
 
157
        """ 
 
158
        Called from the workflow. Used to unset paid state on
 
159
        invoices that were paid with bank transfers which are being cancelled 
 
160
        """
 
161
        for invoice in self.read(cr, uid, ids, ['reconciled'], context):
 
162
            if not invoice['reconciled']:
 
163
                return False
 
164
        return True