2
# -*- coding: utf-8 -*-
3
# -*- encoding: utf-8 -*-
4
##############################################################################
6
# OpenERP, Open Source Management Solution
7
# Copyright (C) 2004-2009 Pexego Sistemas Informáticos. All Rights Reserved
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.
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.
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/>.
23
##############################################################################
27
__author__ = "Borja López Soilán (Pexego)"
34
from osv import fields,osv
35
from tools.translate import _
37
class pxgo_account_move_importer_wizard(osv.osv_memory):
41
Wizard that imports a CSV file into a new account move.
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.
46
The lines of the CSV file are tested to be valid account move lines
47
using the regular expresions set on the wizard.
49
_name = "pxgo_account_move_importer_wizard"
50
_description = "Account move importation wizard"
54
# Account move parameters
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),
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),
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),
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),
94
def _get_default_period_id(self, cr, uid, context=None):
96
Returns the default period to use (based on account.move)
98
period_ids = self.pool.get('account.period').find(cr, uid)
99
return period_ids and period_ids[0] or False
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\-\.\,]*$',
123
def action_import(self, cr, uid, ids, context=None):
125
Imports a CSV file into a new account move using the options from
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!"))
132
account_move_data = self.pool.get('account.move').default_get(cr, uid, ['state', 'name'])
133
account_move_data.update({
135
'journal_id': wiz.journal_id.id,
136
'period_id': wiz.period_id.id,
144
lines_data = account_move_data['line_id']
146
# Decode the file data
147
data = base64.b64decode(wiz.input_file)
152
reader = csv.reader(StringIO.StringIO(data),
153
delimiter=str(wiz.csv_delimiter),
154
quotechar=str(wiz.csv_quotechar))
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:
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]
169
# Ignore invalid records
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):
176
# Clean the input amounts
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, '.'))
182
# Find the account (or fail!)
184
account_ids = self.pool.get('account.account').search(cr, uid, [
185
('code', '=', record_code),
186
('company_id', '=', wiz.company_id.id)
189
raise osv.except_osv(_('Error'), _("Account not found: %s!") % record_code)
192
# Prepare the line data
195
'account_id': account_ids[0],
200
'currency_id': False,
203
'tax_code_id': False,
204
'date_maturity': False,
205
'amount_currency': False,
206
'analytic_account_id': False,
210
# Create a debit line + a credit line if needed
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))
221
netsvc.Logger().notifyChannel('account_move_importer', netsvc.LOG_WARNING,
222
"Invalid record format (ignoring line): %s" % repr(record))
224
netsvc.Logger().notifyChannel('account_move_importer', netsvc.LOG_WARNING,
225
"Too short record (ignoring line): %s" % repr(record))
228
# Finally create the move
229
move_id = self.pool.get('account.move').create(cr, uid, account_move_data)
232
# Show the move to the user
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')
239
resource_id = self.pool.get('ir.model.data').read(cr, uid, model_data_ids, fields=['res_id'])[0]['res_id']
242
'name': _("Imported account moves"),
243
'type': 'ir.actions.act_window',
244
'res_model': 'account.move',
246
'view_mode': 'form,tree',
247
#'view_id': (resource_id, 'View'),
248
'views': [(False,'tree'), (resource_id,'form')],
249
'domain': "[('id', '=', %s)]" % move_id,
255
pxgo_account_move_importer_wizard()