~therp-nl/banking-addons/ba61-lp1098699-fix_clieop_rounding_issue

« back to all changes in this revision

Viewing changes to bank_statement_instant_voucher/model/account_voucher_instant.py

  • Committer: Guewen Baconnier @ Camptocamp
  • Author(s): Stefan Rijnhart
  • Date: 2012-12-10 07:21:06 UTC
  • mfrom: (140.2.5 6.1-bank_statement_instant_voucher)
  • Revision ID: guewen.baconnier@camptocamp.com-20121210072106-xnkuro407242nepe
[IMP] Take advantage of instantly created import transactions on
manually encoded statements

See https://code.launchpad.net/~therp-nl/banking-addons/6.1-lp1066826-matching_wizard_on_manual_statements

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
##############################################################################
 
3
#
 
4
#    OpenERP, Open Source Management Solution
 
5
#    This module copyright (C) 2012 Therp BV (<http://therp.nl>).
 
6
#
 
7
#    This program is free software: you can redistribute it and/or modify
 
8
#    it under the terms of the GNU Affero General Public License as
 
9
#    published by the Free Software Foundation, either version 3 of the
 
10
#    License, or (at your option) any later version.
 
11
#
 
12
#    This program is distributed in the hope that it will be useful,
 
13
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
#    GNU Affero General Public License for more details.
 
16
#
 
17
#    You should have received a copy of the GNU Affero General Public License
 
18
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
#
 
20
##############################################################################
 
21
 
 
22
from openerp.osv import osv, fields
 
23
from openerp.tools.translate import _
 
24
from openerp.addons.decimal_precision import decimal_precision as dp
 
25
 
 
26
 
 
27
class instant_voucher(osv.TransientModel):
 
28
    _name = 'account.voucher.instant'
 
29
    _description = 'Instant Voucher'
 
30
 
 
31
    def cancel(self, cr, uid, ids, context=None):
 
32
        """
 
33
        Delete the voucher and close window
 
34
        """
 
35
        assert len(ids) == 1, "Will only take one resource id"
 
36
        instant = self.browse(cr, uid, ids[0], context=context)
 
37
        if instant.voucher_id:
 
38
            self.pool.get('account.voucher').cancel_voucher(
 
39
                cr, uid, [instant.voucher_id.id], context=context)
 
40
            self.pool.get('account.voucher').unlink(
 
41
                cr, uid, [instant.voucher_id.id], context=context)
 
42
        return {'type': 'ir.actions.act_window_close'}
 
43
 
 
44
    def get_voucher_defaults(
 
45
        self, cr, uid, vals, context=None):
 
46
        """
 
47
        Gather conditional defaults based on given key, value pairs
 
48
 
 
49
        :param vals: dictionary of key, value pairs
 
50
        :returns: dictionary of default values for fields not in vals
 
51
        """
 
52
        values_pool = self.pool.get('ir.values')
 
53
        voucher_pool = self.pool.get('account.voucher')
 
54
        res = {}
 
55
        for (key, val) in vals.iteritems():
 
56
            if val and voucher_pool._all_columns[key].column.change_default:
 
57
                for default in values_pool.get_defaults(
 
58
                    cr, uid, 'account.voucher', '%s=%s' % (key, val)):
 
59
                    if default[1] not in vals:
 
60
                        res[default[1]] = default[2]
 
61
        return res
 
62
 
 
63
    def create_voucher(self, cr, uid, ids, context=None):
 
64
        """
 
65
        Create a fully fledged voucher counterpart for the
 
66
        statement line. User only needs to process taxes and may
 
67
        adapt cost/income account.
 
68
        """
 
69
        assert len(ids) == 1, "Will only take one resource id"
 
70
        voucher_pool = self.pool.get('account.voucher')
 
71
        period_pool = self.pool.get('account.period')
 
72
        instant = self.browse(cr, uid, ids[0], context=context)
 
73
        line = instant.statement_line_id
 
74
        voucher_type = line.amount < 0 and 'purchase' or 'sale'
 
75
        journal_ids = self.pool.get('account.journal').search(
 
76
            cr, uid, [('company_id', '=', line.company_id.id),
 
77
                      ('type', '=', voucher_type)])
 
78
        if not journal_ids:
 
79
            osv.exept_osv(
 
80
                _('Error'),
 
81
                _('No %s journal defined') % voucher_type)
 
82
               
 
83
        journal = self.pool.get('account.journal').browse(
 
84
            cr, uid, journal_ids[0], context=context)
 
85
        if journal.type in ('sale', 'sale_refund'):
 
86
            line_account_id = (journal.default_credit_account_id and
 
87
                          journal.default_credit_account_id.id or False)
 
88
        elif journal.type in ('purchase', 'expense', 'purchase_refund'):
 
89
            line_account_id = (journal.default_debit_account_id and
 
90
                          journal.default_debit_account_id.id or False)
 
91
        vals = {
 
92
            'name': _('Voucher for statement line %s.%s') % (line.statement_id.name, line.name),
 
93
            'reference': line.ref or False,
 
94
            'company_id': line.company_id.id,
 
95
            'partner_id': instant.partner_id.id,
 
96
            'date': line.date or res.get('line.date', False),
 
97
            'account_id': line.account_id.id,
 
98
            'type': voucher_type,
 
99
            'line_ids': [(0, 0, {'amount': abs(line.amount),
 
100
                                 'account_id': line_account_id,
 
101
                                 'type': line.amount < 0 and 'dr' or 'cr',
 
102
                                 'name': line.ref or False,
 
103
                                 })],
 
104
            'amount': line.amount and abs(line.amount) or res.get('amount', False),
 
105
            'journal_id': journal_ids[0],
 
106
            }
 
107
        if vals['date']:
 
108
            period_ids = period_pool.find(cr, uid, vals['date'], context=context)
 
109
            if period_ids:
 
110
                vals['period_id'] = period_ids[0]
 
111
        vals.update(self.get_voucher_defaults(cr, uid, vals, context=context))
 
112
 
 
113
        voucher_id = voucher_pool.create(
 
114
            cr, uid, vals, context=context)
 
115
        self.write(
 
116
            cr, uid, ids[0], 
 
117
            {'voucher_id': voucher_id,
 
118
             'state': 'ready',
 
119
             'type': voucher_type,
 
120
             }, context=context)
 
121
        return {
 
122
            'name': self._description,
 
123
            'view_type': 'form',
 
124
            'view_mode': 'form',
 
125
            'res_model': self._name,
 
126
            'domain': [],
 
127
            'context': context,
 
128
            'type': 'ir.actions.act_window',
 
129
            'target': 'new',
 
130
            'res_id': ids[0],
 
131
            'nodestroy': False,
 
132
            }
 
133
 
 
134
    def dummy(self, cr, uid, ids, context=None):
 
135
        return {
 
136
            'name': self._description,
 
137
            'view_type': 'form',
 
138
            'view_mode': 'form',
 
139
            'res_model': self._name,
 
140
            'domain': [],
 
141
            'context': context,
 
142
            'type': 'ir.actions.act_window',
 
143
            'target': 'new',
 
144
            'res_id': ids[0],
 
145
            'nodestroy': False,
 
146
            }
 
147
 
 
148
    def default_get(self, cr, uid, fields_list, context=None):
 
149
        """
 
150
        Gather sane default values from the originating statement line
 
151
        """
 
152
        res = super(instant_voucher, self).default_get(
 
153
            cr, uid, fields_list, context=context)
 
154
        if 'statement_line_id' in fields_list:
 
155
            res['statement_line_id'] = (
 
156
                context.get('active_id') or
 
157
                context.get('active_ids') and context.get('active_ids')[0])
 
158
            if not res['statement_line_id']:
 
159
                raise osv.except_osv(
 
160
                    _('Error'),
 
161
                    _('Cannot determine statement line'))
 
162
            line = self.pool.get('account.bank.statement.line').browse(
 
163
                cr, uid, res['statement_line_id'], context=context)
 
164
            if 'balance' in fields_list:
 
165
                res['balance'] = line.amount
 
166
            if 'ref' in fields_list:
 
167
                res['ref'] = line.ref
 
168
            if 'partner_id' in fields_list:
 
169
                if line.partner_id:
 
170
                    res['partner_id'] = line.partner_id.id
 
171
        return res
 
172
 
 
173
    def _get_balance(self, cr, uid, ids, field_name, args, context=None):
 
174
        """
 
175
        Compute the expected residual
 
176
        TODO: currency conversion
 
177
        """
 
178
        res = {}
 
179
        for instant in self.browse(cr, uid, ids, context=context):
 
180
            if instant.voucher_id and instant.voucher_id.state == 'posted':
 
181
                amount = instant.statement_line_id.amount
 
182
                counteramount = 0.0
 
183
                for line in instant.voucher_id.move_ids:
 
184
                    if line.account_id.id == instant.statement_line_id.account_id.id:
 
185
                        counteramount = line.debit - line.credit
 
186
                for line in instant.voucher_id.move_ids:
 
187
                    if line.account_id.id == instant.statement_line_id.account_id.id:
 
188
                        counteramount = line.debit - line.credit
 
189
            else:
 
190
                amount = abs(instant.statement_line_id.amount)
 
191
                counteramount = abs(instant.voucher_id and instant.voucher_id.amount or 0.0)
 
192
            res[instant.id] = amount - counteramount
 
193
        return res
 
194
 
 
195
    def confirm(self, cr, uid, ids, context=None):
 
196
        """
 
197
        Post the voucher if necessary
 
198
        Post the voucher's move lines if necessary
 
199
        Sanity checks on currency and residual = 0.0
 
200
        
 
201
        If the account_banking module is installed, perform matching
 
202
        and reconciliation. If not, the user is left to manual
 
203
        reconciliation of OpenERP.
 
204
        """
 
205
        assert len(ids) == 1, "Will only take one resource id"
 
206
        statement_line_obj = self.pool.get('account.bank.statement.line')
 
207
        voucher_obj = self.pool.get('account.voucher')
 
208
        move_obj = self.pool.get('account.move')
 
209
        instant = self.browse(cr, uid, ids[0], context=context)
 
210
        voucher_currency = (instant.voucher_id.currency_id and
 
211
                            instant.voucher_id.currency_id or
 
212
                            instant.voucher_id.company_id.currency_id)
 
213
        if (instant.statement_line_id.statement_id.currency.id !=
 
214
            voucher_currency.id):
 
215
            raise osv.except_osv(
 
216
                _("Error"),
 
217
                _("Currency on the bank statement line needs to be the "
 
218
                  "same as on the voucher. Currency conversion is not yet "
 
219
                  "supported."))
 
220
        if instant.voucher_id.state != 'posted':
 
221
            voucher_obj.proforma_voucher(
 
222
                cr, uid, [instant.voucher_id.id], context=context)
 
223
            instant.refresh()
 
224
            if instant.voucher_id.state != 'posted':
 
225
                raise osv.except_osv(
 
226
                    _("Error"),
 
227
                    _("The voucher could not be posted."))
 
228
        if instant.voucher_id.move_id.state != 'posted':
 
229
            move_obj.post(
 
230
                cr, uid, [instant.voucher_id.move_id.id], context=context)
 
231
            instant.refresh()
 
232
            if instant.voucher_id.move_id.state != 'posted':
 
233
                raise osv.except_osv(
 
234
                    _("Error"),
 
235
                    _("The voucher's move line could not be posted."))
 
236
        if not self.pool.get('res.currency').is_zero(
 
237
            cr, uid, voucher_currency, instant.balance):
 
238
            raise osv.except_osv(
 
239
                _("Error"),
 
240
                _("The amount on the bank statement line needs to be the "
 
241
                  "same as on the voucher. Write-off is not yet "
 
242
                  "supported."))
 
243
        # Banking Addons integration:
 
244
        # Gather the info needed to match the bank statement line
 
245
        # and trigger its posting and reconciliation.
 
246
        if 'import_transaction_id' in statement_line_obj._columns:
 
247
            if instant.statement_line_id.state == 'confirmed':
 
248
                raise osv.except_osv(
 
249
                    _("Error"),
 
250
                    _("Cannot match a confirmed statement line"))
 
251
            if not instant.statement_line_id.import_transaction_id:
 
252
                statement_line_obj.create_instant_transaction(
 
253
                    cr, uid, instant.statement_line_id.id, context=context)
 
254
                instant.statement_line_id.refresh()
 
255
            for line in instant.voucher_id.move_ids:
 
256
                if line.account_id.id == instant.statement_line_id.account_id.id:
 
257
                    self.pool.get('banking.import.transaction').write(
 
258
                        cr, uid, instant.statement_line_id.import_transaction_id.id,
 
259
                        {
 
260
                            'move_line_id': line.id,
 
261
                            'move_line_ids': [(6, 0, [line.id])],
 
262
                            'match_type': 'move',
 
263
                            'invoice_id': False,
 
264
                            'invoice_ids': [(6, 0, [])],
 
265
                            }, context=context)
 
266
 
 
267
                    statement_line_obj.confirm(
 
268
                        cr, uid, [instant.statement_line_id.id], context=context)
 
269
                    break
 
270
        return {'type': 'ir.actions.act_window_close'}
 
271
 
 
272
    _columns = {
 
273
        'balance': fields.function(
 
274
                    _get_balance,
 
275
                    type='float',
 
276
                    digits_compute=dp.get_precision('Account'),
 
277
                    string="Balance",),
 
278
        'partner_id': fields.many2one(
 
279
            'res.partner',
 
280
            'Partner',
 
281
            required=True),
 
282
        'statement_line_id': fields.many2one(
 
283
            'account.bank.statement.line',
 
284
            'Bank statement line',
 
285
            readonly=True),
 
286
        'ref': fields.related(
 
287
            'statement_line_id', 'ref',
 
288
            type="char", size="48",
 
289
            readonly=True,
 
290
            string="Reference"),
 
291
        'voucher_id': fields.many2one(
 
292
            'account.voucher',
 
293
            'Voucher',
 
294
            readonly=True),
 
295
        'state': fields.selection(
 
296
            [('init', 'init'),
 
297
             ('ready', 'ready'),
 
298
             ('confirm', 'confirm')],
 
299
            'State'),
 
300
        'type': fields.selection(
 
301
            [('sale', 'Sale'),
 
302
             ('purchase', 'Purchase')],
 
303
            'Voucher type'),
 
304
        }
 
305
 
 
306
    _defaults = {'state': 'init'}