~openerp-commiter/openobject-addons/extra-6.0

« back to all changes in this revision

Viewing changes to pxgo_account_admin_tools/account_move_importer_wizard.py

  • Committer: Borja L.S.
  • Date: 2010-05-20 17:10:42 UTC
  • Revision ID: borjals@pexego.es-20100520171042-iot70m8r9b80uyko
[ADD] pxgo_account_admin_tools: New accounting wizards for administrators
  
  Account Adminitration Tools:
  
- Adds a wizard to import accounts from CSV files. This may be useful
  to import the initial accounts into OpenERP.

- Adds a wizard to import account moves from CSV files. This may be 
  useful to import the initial balance into OpenERP.

- Adds a wizard to set the receivable/payable account of the partners,
  in moves and invoices where a generic receivable/payable account
  was used instead.

- Adds a wizard to revalidate confirmed account moves so their analytic
  lines are regenerated. This may be used to fix the data after bugs 
  like https://bugs.launchpad.net/openobject-addons/+bug/582988
  The wizard also lets you find account moves missing their analytic 
  lines.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import netsvc
 
2
# -*- coding: utf-8 -*-
 
3
# -*- encoding: utf-8 -*-
 
4
##############################################################################
 
5
#
 
6
#    OpenERP, Open Source Management Solution
 
7
#    Copyright (C) 2004-2009 Pexego Sistemas Informáticos. All Rights Reserved
 
8
#    $Id$
 
9
#
 
10
#    This program is free software: you can redistribute it and/or modify
 
11
#    it under the terms of the GNU General Public License as published by
 
12
#    the Free Software Foundation, either version 3 of the License, or
 
13
#    (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 General Public License for more details.
 
19
#
 
20
#    You should have received a copy of the GNU General Public License
 
21
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
22
#
 
23
##############################################################################
 
24
"""
 
25
Account Move Importer
 
26
"""
 
27
__author__ = "Borja López Soilán (Pexego)"
 
28
 
 
29
import time
 
30
import csv
 
31
import base64
 
32
import StringIO
 
33
import re
 
34
from osv import fields,osv
 
35
from tools.translate import _
 
36
 
 
37
class pxgo_account_move_importer_wizard(osv.osv_memory):
 
38
    """
 
39
    Account Move Importer
 
40
 
 
41
    Wizard that imports a CSV file into a new account move.
 
42
 
 
43
    The CSV file is expected to have at least the account code, a reference
 
44
    (description of the move line), the debit and the credit.
 
45
 
 
46
    The lines of the CSV file are tested to be valid account move lines
 
47
    using the regular expresions set on the wizard.
 
48
    """
 
49
    _name = "pxgo_account_move_importer_wizard"
 
50
    _description = "Account move importation wizard"
 
51
 
 
52
    _columns = {
 
53
        #
 
54
        # Account move parameters
 
55
        #
 
56
        'company_id': fields.many2one('res.company', 'Company', required=True),
 
57
        'ref': fields.char('Ref', size=64, required=True),
 
58
        'period_id': fields.many2one('account.period', 'Period', required=True),
 
59
        'journal_id': fields.many2one('account.journal', 'Journal', required=True),
 
60
        'date': fields.date('Date', required=True),
 
61
 
 
62
        'type': fields.selection([
 
63
            ('pay_voucher','Cash Payment'),
 
64
            ('bank_pay_voucher','Bank Payment'),
 
65
            ('rec_voucher','Cash Receipt'),
 
66
            ('bank_rec_voucher','Bank Receipt'),
 
67
            ('cont_voucher','Contra'),
 
68
            ('journal_sale_vou','Journal Sale'),
 
69
            ('journal_pur_voucher','Journal Purchase'),
 
70
            ('journal_voucher','Journal Voucher'),
 
71
        ],'Type', select=True, required=True),
 
72
 
 
73
        #
 
74
        # Input file
 
75
        #
 
76
        'input_file': fields.binary('File', filters="*.csv", required=True),
 
77
        'input_file_name': fields.char('File name', size=256),
 
78
        'csv_delimiter': fields.char('Delimiter', size=1, required=True),
 
79
        'csv_quotechar': fields.char('Quote', size=1, required=True),
 
80
        'csv_decimal_separator': fields.char('Decimal sep.', size=1, required=True),
 
81
        'csv_thousands_separator': fields.char('Thousands sep.', size=1, required=True),
 
82
 
 
83
        'csv_code_index': fields.integer('Code field', required=True),
 
84
        'csv_code_regexp': fields.char('Code regexp', size=32, required=True),
 
85
        'csv_ref_index': fields.integer('Ref field', required=True),
 
86
        'csv_ref_regexp': fields.char('Ref regexp', size=32, required=True),
 
87
        'csv_debit_index': fields.integer('Debit field', required=True),
 
88
        'csv_debit_regexp': fields.char('Debit regexp', size=32, required=True),
 
89
        'csv_credit_index': fields.integer('Credit field', required=True),
 
90
        'csv_credit_regexp': fields.char('Credit regexp', size=32, required=True),
 
91
    }
 
92
 
 
93
 
 
94
    def _get_default_period_id(self, cr, uid, context=None):
 
95
        """
 
96
        Returns the default period to use (based on account.move)
 
97
        """
 
98
        period_ids = self.pool.get('account.period').find(cr, uid)
 
99
        return period_ids and period_ids[0] or False
 
100
    
 
101
 
 
102
    _defaults = {
 
103
        'company_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context).company_id.id,
 
104
        'period_id': _get_default_period_id,
 
105
        'date': lambda *a: time.strftime('%Y-%m-%d'),
 
106
        'type': lambda *a: 'journal_voucher', # Based on account move
 
107
        'csv_delimiter': lambda *a: ';',
 
108
        'csv_quotechar': lambda *a: '"',
 
109
        'csv_decimal_separator': lambda *a: '.',
 
110
        'csv_thousands_separator': lambda *a: ',',
 
111
        'csv_code_index': lambda *a: 0,
 
112
        'csv_ref_index': lambda *a: 1,
 
113
        'csv_debit_index': lambda *a: 2,
 
114
        'csv_credit_index': lambda *a: 3,
 
115
        'csv_code_regexp': lambda *a: r'^[0-9]+$',
 
116
        'csv_ref_regexp': lambda *a: r'^.*$',
 
117
        'csv_debit_regexp': lambda *a: r'^[0-9\-\.\,]*$',
 
118
        'csv_credit_regexp': lambda *a: r'^[0-9\-\.\,]*$',
 
119
    }
 
120
 
 
121
 
 
122
 
 
123
    def action_import(self, cr, uid, ids, context=None):
 
124
        """
 
125
        Imports a CSV file into a new account move using the options from
 
126
        the wizard.
 
127
        """
 
128
        for wiz in self.browse(cr, uid, ids, context):
 
129
            if not wiz.input_file:
 
130
                raise osv.except_osv(_('UserError'), _("You need to select a file!"))
 
131
 
 
132
            account_move_data = self.pool.get('account.move').default_get(cr, uid, ['state', 'name'])
 
133
            account_move_data.update({
 
134
                    'ref': wiz.ref,
 
135
                    'journal_id': wiz.journal_id.id,
 
136
                    'period_id': wiz.period_id.id,
 
137
                    'date': wiz.date,
 
138
                    'type': wiz.type,
 
139
                    'line_id': [],
 
140
                    'partner_id': False,
 
141
                    'to_check': 0
 
142
                })
 
143
 
 
144
            lines_data = account_move_data['line_id']
 
145
 
 
146
            # Decode the file data
 
147
            data = base64.b64decode(wiz.input_file)
 
148
 
 
149
            #
 
150
            # Read the file
 
151
            #
 
152
            reader = csv.reader(StringIO.StringIO(data),
 
153
                                delimiter=str(wiz.csv_delimiter),
 
154
                                quotechar=str(wiz.csv_quotechar))
 
155
 
 
156
            for record in reader:
 
157
                # Ignore short records
 
158
                if len(record) > wiz.csv_code_index \
 
159
                        and len(record) > wiz.csv_ref_index \
 
160
                        and len(record) > wiz.csv_debit_index \
 
161
                        and len(record) > wiz.csv_credit_index:
 
162
 
 
163
                    record_code = record[wiz.csv_code_index]
 
164
                    record_ref =  record[wiz.csv_ref_index]
 
165
                    record_debit =  record[wiz.csv_debit_index]
 
166
                    record_credit = record[wiz.csv_credit_index]
 
167
 
 
168
                    #
 
169
                    # Ignore invalid records
 
170
                    #
 
171
                    if re.match(wiz.csv_code_regexp, record_code) \
 
172
                            and re.match(wiz.csv_ref_regexp, record_ref) \
 
173
                            and re.match(wiz.csv_debit_regexp, record_debit) \
 
174
                            and re.match(wiz.csv_credit_regexp, record_credit):
 
175
                        #
 
176
                        # Clean the input amounts
 
177
                        #
 
178
                        record_debit = float(record_debit.replace(wiz.csv_thousands_separator, '').replace(wiz.csv_decimal_separator, '.'))
 
179
                        record_credit = float(record_credit.replace(wiz.csv_thousands_separator, '').replace(wiz.csv_decimal_separator, '.'))
 
180
 
 
181
                        #
 
182
                        # Find the account (or fail!)
 
183
                        #
 
184
                        account_ids = self.pool.get('account.account').search(cr, uid, [
 
185
                                    ('code', '=', record_code),
 
186
                                    ('company_id', '=', wiz.company_id.id)
 
187
                                ])
 
188
                        if not account_ids:
 
189
                            raise osv.except_osv(_('Error'), _("Account not found: %s!") % record_code)
 
190
 
 
191
                        #
 
192
                        # Prepare the line data
 
193
                        #
 
194
                        line_data = {
 
195
                            'account_id': account_ids[0],
 
196
                            'debit': 0.0,
 
197
                            'credit': 0.0,
 
198
                            'name': record_ref,
 
199
                            'ref': False,
 
200
                            'currency_id': False,
 
201
                            'tax_amount': False,
 
202
                            'partner_id': False,
 
203
                            'tax_code_id': False,
 
204
                            'date_maturity': False,
 
205
                            'amount_currency': False,
 
206
                            'analytic_account_id': False,
 
207
                        }
 
208
 
 
209
                        #
 
210
                        # Create a debit line + a credit line if needed
 
211
                        #
 
212
                        line_data_debit = line_data.copy()
 
213
                        line_data_credit = line_data
 
214
                        if record_debit != 0.0:
 
215
                            line_data_debit['debit'] = record_debit
 
216
                            lines_data.append((0, 0, line_data_debit))
 
217
                        if record_credit != 0.0:
 
218
                            line_data_credit['credit'] = record_credit
 
219
                            lines_data.append((0, 0, line_data_credit))
 
220
                    else:
 
221
                        netsvc.Logger().notifyChannel('account_move_importer', netsvc.LOG_WARNING,
 
222
                                "Invalid record format (ignoring line): %s" % repr(record))
 
223
                else:
 
224
                    netsvc.Logger().notifyChannel('account_move_importer', netsvc.LOG_WARNING,
 
225
                            "Too short record (ignoring line): %s" % repr(record))
 
226
 
 
227
 
 
228
        # Finally create the move
 
229
        move_id = self.pool.get('account.move').create(cr, uid, account_move_data)
 
230
        
 
231
        #
 
232
        # Show the move to the user
 
233
        #
 
234
        model_data_ids = self.pool.get('ir.model.data').search(cr, uid, [
 
235
                    ('model','=','ir.ui.view'),
 
236
                    ('module','=','account'),
 
237
                    ('name','=','view_move_form')
 
238
                ])
 
239
        resource_id = self.pool.get('ir.model.data').read(cr, uid, model_data_ids, fields=['res_id'])[0]['res_id']
 
240
 
 
241
        return {
 
242
            'name': _("Imported account moves"),
 
243
            'type': 'ir.actions.act_window',
 
244
            'res_model': 'account.move',
 
245
            'view_type': 'form',
 
246
            'view_mode': 'form,tree',
 
247
            #'view_id': (resource_id, 'View'),
 
248
            'views': [(False,'tree'), (resource_id,'form')],
 
249
            'domain': "[('id', '=', %s)]" % move_id,
 
250
        }
 
251
 
 
252
 
 
253
 
 
254
 
 
255
pxgo_account_move_importer_wizard()
 
256
 
 
257