1
# -*- coding: utf-8 -*-
2
###############################################################################
4
# Asgard Ledger Export (ALE) module,
5
# Copyright (C) 2005 - 2013
6
# Héonium (http://www.heonium.com). All Right Reserved
8
# Asgard Ledger Export (ALE) module
9
# is free software: you can redistribute it and/or modify it under the terms
10
# of the Affero GNU General Public License as published by the Free Software
11
# Foundation, either version 3 of the License, or (at your option) any later
14
# Asgard Ledger Export (ALE) module
15
# is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
16
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
17
# PARTICULAR PURPOSE. See the Affero GNU General Public License for more
20
# You should have received a copy of the Affero GNU General Public License
21
# along with this program. If not, see <http://www.gnu.org/licenses/>.
23
###############################################################################
31
from osv import fields, osv
32
from tools.translate import _
33
from heo_common.hnm_lib import *
34
from heo_common.files_tools import *
38
('unix', 'Unix', '\n'),
39
('windows', 'Windows', '\r\n'),
40
('nothing', 'Nothing', '')
44
class asgard_ledger_export(osv.osv):
46
Configuration of export
49
_name = "asgard.ledger.export"
50
_description = "Define export"
53
'name': fields.char('Name', size=64, translate=True, required=True),
54
'separator': fields.char('Separator', size=8, help="Separator field, leave blank if you dont have"),
55
'end_line': fields.selection([('unix', 'Unix'), ('windows','Windows'),('nothing','Nothing')],'End of line'),
56
'file_header': fields.text('File header', help="Text used to begin file."),
57
'extension': fields.char('Extension', size=5, help="String add on end of name file (without the dot).", required=True),
58
'alej_line': fields.one2many('asgard.ledger.export.journal', 'asgard_ledger_id', 'Asgard Ledger Journal Lines'),
59
'alef_line': fields.one2many('asgard.ledger.export.fields', 'asgard_ledger_id', 'Asgard Ledger Field Lines'),
60
'encoding': fields.selection([('utf-8', 'UTF-8'), ('iso-8859-15','ISO-8859-15')], 'Encoding', help="Encoding of exported file"),
61
'active': fields.boolean('Active'),
62
'company_id': fields.many2one('res.company', 'Company', required=True),
66
'active': lambda *a: True,
67
'end_line': lambda *a: 'unix',
68
'extension': lambda *a: 'csv',
69
'file_header': lambda *a: '',
70
'separator': lambda *a: ';',
71
'encoding': lambda *a: 'utf-8',
72
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'account.account', context=c),
76
def get_end_line(self, cr, uid, name, context={}):
79
for endline in END_LINE:
80
if endline[0] == name:
86
def get_building_line(self, cr, uid, ids, context={}):
88
Get the table for building line
91
pool = pooler.get_pool(cr.dbname)
92
alef_obj = pool.get('asgard.ledger.export.fields')
95
for config in self.browse(cr, uid, ids, context):
96
result = alef_obj.get_building_field(cr, uid, map(lambda x:x.id,config.alef_line))
101
asgard_ledger_export()
104
class asgard_ledger_export_journal(osv.osv):
106
Table d'association entre les journaux comptable d'OpenERP et les noms des
110
_name = "asgard.ledger.export.journal"
111
_description = "Link beetween OpenERP's journal and Sage journal"
114
'name': fields.char('Name', size=64, translate=True, required=True),
115
'asgard_ledger_id': fields.many2one('asgard.ledger.export', 'Asgard Ledger Ref', required=True, ondelete="cascade", select=True,
116
help="Associated configuration"),
117
'active': fields.boolean('Active'),
118
# Association journaux
119
'journal_name': fields.char('Journal name', size=10, required=True,
120
help="Journal name in target software. Warning, you must put exact name"),
121
'journal_id': fields.many2one('account.journal', 'Journal', required=True, ondelete="cascade",
122
help="Select the associated OpenERP's journal"),
123
'company_id': fields.many2one('res.company', 'Company', required=True),
127
'active': lambda *a: True,
128
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'account.account', context=c),
132
('oerp_journal_uniq', 'unique (asgard_ledger_id,journal_id)', _('There is already an association with this journal !')),
137
asgard_ledger_export_journal()
140
class asgard_ledger_export_fields(osv.osv):
141
_name = "asgard.ledger.export.fields"
142
_description = "Definition of fields"
147
'name': fields.char('Name', size=64, translate=True, required=True, help="Name of this definition"),
148
'asgard_ledger_id': fields.many2one('asgard.ledger.export', 'Asgard Ledger Ref', required=True, ondelete="cascade", select=True,
149
help="Asgard associated configuration"),
150
'sequence': fields.integer('Sequence', help="Order of field (in file target)."),
151
'type_field': fields.selection([
152
('account_field', 'Account field'),
153
('text_field', 'Text field'),
154
('internal_field', 'Internal field'),
155
('build_field', 'Build field')], 'Field type',
156
help="Select the type of field\n \
157
Account field : Field of object 'Account'.\n \
158
Internal field : Select field from list.\n \
159
Text field : Only text.\n \
160
Build field : Python code for build value. You can use 'object' and 'time'."),
162
'field_account': fields.many2one('ir.model.fields', 'Fields', domain=[
163
('model', '=', 'account.move.line')
165
help="Select which account filed you want export."),
166
'field_indirection': fields.char('Indirection', size=128, help="Indirection line if fields is a key (ex : .name) "),
167
'field_text': fields.char('Value', size=512, help="You can put here text (if 'Field type' on 'Text field') or python expression (if 'Field type' on 'Build field')."),
168
'field_internal': fields.selection([
169
('journal_code', 'Target Journal code'),
170
('entry_num', '# Entry (relatif)')], 'Computed field',
171
help="Target Jounal Code: Journal code when you run wizard. '# Entry': Number of entry (relative to export)"),
173
'build_cmd': fields.char('Build command', size=64, required=True,
174
help="Define with folowing sample : \n \
175
['string','%s'] : For standard string field. \n \
176
['string','%20s'] : For right justify string on 20 char. \n \
177
['date','%d/%m/%Y'] : For date formated in french. \n \
178
['float','%f'], : ... \n \
179
['int','%d'] : ..."),
180
'position': fields.integer('Position', required=True, help='Position in exported file (not use)'),
181
'lenght': fields.integer('Size (lenght)', required=True, help='Lenght of field in exported file.'),
182
'error_label': fields.char('Error label', size=64, help="Fill this information if the content of the field shouldn't empty. This information ..."),
184
'active': fields.boolean('Active'),
185
'company_id': fields.many2one('res.company', 'Company', required=True),
189
'build_cmd': lambda *a: "['string','%s']",
190
'type_field': lambda *a: 'internal_field',
191
'field_text': lambda *a: '',
192
'field_indirection': lambda *a: '',
193
'position': lambda *a: 0,
194
'active': lambda *a: True,
195
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'account.account', context=c),
199
def get_building_field(self, cr, uid, ids, context={}):
201
Return a list of informations need to build each field.
205
for line in self.browse(cr, uid, ids, context):
207
# Ajout du type de champs ...
208
if line.type_field == 'account_field':
209
temp.append([line.type_field, line.field_account.id, line.field_indirection or ''])
210
elif line.type_field in ['text_field', 'build_field']:
211
temp.append([line.type_field, line.field_text])
213
temp.append([line.type_field, line.field_internal])
214
# ... ajout des infos complémentaire, position, longueur, ...
215
temp.extend([line.position, line.lenght, eval(line.build_cmd)])
216
# ... ajout du libelé si besoin ...
218
temp.extend(line.error_label)
224
asgard_ledger_export_fields()
227
class asgard_ledger_export_statement(osv.osv):
229
Liste des exports déja réalisé
231
def _balance(self, cursor, user, ids, name, attr, context=None):
234
for statement in self.browse(cursor, user, ids, context=context):
235
res[statement.id] = 0.0
236
for ales_line_id in statement.ales_line_ids:
237
if ales_line_id.debit > 0:
238
res[statement.id] += ales_line_id.debit
240
res[statement.id] -= ales_line_id.credit
242
res[r] = round(res[r], 2)
246
def _get_period(self, cr, uid, context={}):
247
periods = self.pool.get('account.period').find(cr, uid)
255
_name = "asgard.ledger.export.statement"
256
_description = "Asgard Ledger Export Statement"
259
'name': fields.char('Name', size=64, translate=True, required=True, readonly=True, states={'draft':[('readonly',False)]}),
260
'date': fields.date('Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
261
'journal_period_id': fields.many2many('account.journal.period', 'asgard_export_ledger_journal_period_rel',
262
'statement_id', 'journal_period_id', 'Journal/Period', readonly=True, states={'draft':[('readonly',False)]},
263
help="Select which period you want export."),
264
'ale_id': fields.many2one('asgard.ledger.export', 'Asgard Ledger', required=True, readonly=True,
265
states={'draft':[('readonly', False)]}),
266
'ales_line_ids': fields.one2many('asgard.ledger.export.statement.line', 'ales_id', 'ALE Lines',
267
readonly=True, states={'draft':[('readonly',False)]}),
268
'balance': fields.function(_balance, method=True, string='Balance'),
269
'state': fields.selection([
270
('draft', _('Draft')),
271
('confirm', _('Confirmed')),
273
('cancel', _('Cancelled'))
275
'State', required=True,
276
states={'confirm': [('readonly', True)]}, readonly="1"),
277
'company_id': fields.many2one('res.company', 'Company', required=True),
281
'name': lambda self, cr, uid, context=None: \
282
self.pool.get('ir.sequence').get(cr, uid, 'asgard.ledger.export.statement'),
283
'date': lambda *a: time.strftime('%Y-%m-%d'),
284
'state': lambda *a: 'draft',
285
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'account.account', context=c),
289
def onchange_ale_id(self, cr, uid, ids, ale_id):
293
ale_obj = self.pool.get('asgard.ledger.export')
294
journal_period_obj = self.pool.get('account.journal.period')
296
ale = ale_obj.browse(cr, uid, ale_id)
297
journal_ids = [x.journal_id.id for x in ale.alej_line]
299
journal_period_ids = journal_period_obj.search(cr, uid, [
300
('journal_id', 'in', journal_ids)
303
return {'context': {'journal_id': journal_ids}}
306
def action_confirm(self, cr, uid, ids, *args):
309
# Si la balance est égale a zéro et qu'il y a des lignes à exporter
310
for ale in self.browse(cr, uid, ids, context={}):
311
if len(ale.ales_line_ids) == 0:
312
raise osv.except_osv(
314
_("You have to add some line to export.")
317
if ale.balance == 0.0:
318
result = self.write(cr, uid, ids, {'state':'confirm'})
320
raise osv.except_osv(
322
_("Balance need to be equal to zero.")
328
def action_draft(self, cr, uid, ids, *args):
329
return self.write(cr, uid, ids, {'state':'draft'})
332
def action_cancel(self, cr, uid, ids, *args):
333
return self.write(cr, uid, ids, {'state':'cancel'})
336
def action_populate(self, cr, uid, ids, *args):
342
pool = pooler.get_pool(cr.dbname)
343
journal_period_obj = pool.get('account.journal.period')
344
ales_line_obj = pool.get('asgard.ledger.export.statement.line')
346
for ale in self.browse(cr, uid, ids, context={}):
347
if not ale.journal_period_id:
348
raise osv.except_osv(_('No Journal/Period selected !'), _('You have to select Journal/Period before populate line.'))
349
jp_ids = journal_period_obj.read(cr, uid, map(lambda x:x.id,ale.journal_period_id), ['journal_id','period_id'])
355
cr.execute('SELECT id \
359
period_id = %s AND journal_id = %s AND id NOT IN \
360
(SELECT move_line_id FROM asgard_ledger_export_statement_line) \
361
LIMIT 500 OFFSET %s',
362
(jp['period_id'][0],jp['journal_id'][0],str(offset)))
363
results = map(lambda x:x[0], cr.fetchall())
364
for result in results:
365
ales_line_id = ales_line_obj.create(cr, uid, {
367
'move_line_id': result})
373
def _fjournal_code(self, cr, uid, ids, **kwargs):
374
alej_obj = pooler.get_pool(cr.dbname).get('asgard.ledger.export.journal')
376
alej_line_id = alej_obj.search(cr, uid, [
377
('asgard_ledger_id', '=', kwargs['data'][1].ales_id.ale_id.id),
378
('journal_id', '=', kwargs['data'][1].journal_id.id),
379
('active', '=', True)])
382
raise osv.except_osv(
383
_('No Journal linked !'),
384
_("You have to define OpenERP journal linked with target name in asgard ledger configuration")
387
return alej_obj.read(cr, uid, alej_line_id, ['journal_name'])[0]['journal_name']
390
def _faccount_field(self, cr, uid, ids, **kwargs):
392
Cette fonction peut être remplacée par '_fbuild_field', mais elle est
393
gardé pour éviter d'avoir mettre une expression python pour les champs
394
simple de l'écriture et leur indirection de niveau 1
395
parameters : {'data':[<type_field>,<python_expression>], 'line':<browse_line_record>}
396
return : Value of field
398
imf_obj = pooler.get_pool(cr.dbname).get('ir.model.fields')
401
# Récupération du nom du champs par rapport à l'object 'ir_model_fields' ...
402
imf = imf_obj.read(cr, uid, [kwargs['data'][1]], ['name'])[0]['name']
404
# ... Contruction du champs et affectation à 'bf.
405
t = "kwargs['line'].move_line_id.%s%s" % (str(imf), str(kwargs['data'][2]))
407
if isinstance(eval(t), float):
413
def _ftext_field(self, cr, uid, ids, **kwargs):
415
parameters : {'data':[<type_field>,<python_expression>], 'line':<browse_line_record>}
416
return : Text value of data given in parameters
418
return kwargs['data'][1]
421
def _fbuild_field(self, cr, uid, ids, **kwargs):
423
Evalue le champ contenant une expression Python
424
parameters : {'data':[<type_field>,<python_expression>], 'line':<browse_line_record>}
425
return : result of python expression evaluation.
429
if kwargs['data'][1]:
430
# Récupération de l'objet 'account_move_line'
431
obj = kwargs['line'].move_line_id
432
# Évaluation de l'expression python sur l'objet
433
result = eval(kwargs['data'][1], {'object': obj, 'time': time})
438
def _finternal_field(self, cr, uid, ids, **kwargs):
439
return self.get_value(cr, uid, ids, data=[kwargs['data'][1], kwargs['line']])
442
def get_value(self, cr, uid, ids, **kwargs):
444
Call correct method depend on parameters
445
parameters : {'data':[<type_field>,<python_expression>], 'line':<browse_line_record>}
446
return : result of method called
448
method_name = '_f' + str(kwargs['data'][0]).lower()
450
method = getattr(self, method_name)
451
except AttributeError:
452
print ("The method for verify '%s' type isn't defined (You must define it in class 'asgard_ledger_export_statement' of module '%s')." % (method_name,__name__))
454
return method(cr, uid, ids, **kwargs)
457
def action_export(self, cr, uid, ids, *args):
459
For each entry line build the text value
461
pool = pooler.get_pool(cr.dbname)
462
mod_obj = self.pool.get('ir.model.data')
463
ale_obj = pool.get('asgard.ledger.export')
464
alej_obj = pool.get('asgard.ledger.export.journal')
465
attach_obj = pool.get('ir.attachment')
468
for ales in self.browse(cr, uid, ids):
470
# We need first to check if we have other attchments
471
# (Attachments are limited to 1 per statement)
473
attach_ids = attach_obj.search(cr, uid, [
474
('name', 'like', 'export : %'),
475
('res_model', '=', 'asgard.ledger.export.statement'),
476
('res_id', '=', ales.id),
479
if len(attach_ids) >= 1:
480
raise osv.except_osv(
482
_("You cannot have more than one attachment !")
485
# Initalisation de l'encodage du fichier
486
enc = generic_encode(
487
file_header=ales.ale_id.file_header,
488
separator=ales.ale_id.separator,
489
ext=ales.ale_id.extension,
490
ending=ale_obj.get_end_line(cr, uid, ales.ale_id.end_line),
491
encoding=ales.ale_id.encoding)
493
# Recupération du tableau de construction de ligne
494
build_line = ale_obj.get_building_line(cr, uid, [ales.ale_id.id])
496
# pour chaque ligne écritures
497
for ales_line in ales.ales_line_ids:
498
# Pour chaque construction de champs dans le tableau 'build_line'
499
# Duplication du tableau de génération de ligne de texte
500
bl = copy.deepcopy(build_line)
502
bf[0] = self.get_value(cr, uid, ids, data=bf[0], line=ales_line)
503
result = enc.write_file_in_flow(bl)
508
fp = open(os.path.join(enc.dir_tmp, enc.filetmp), 'r')
509
file_data = fp.read()
510
attach_id = attach_obj.create(cr, uid, {
511
'name': 'export : ' + ales.name,
512
'datas': base64.encodestring(file_data),
513
'datas_fname': enc.filetmp,
514
'res_model': 'asgard.ledger.export.statement',
519
self.write(cr, uid, ids, {'state': 'done'})
524
asgard_ledger_export_statement()
527
class asgard_ledger_export_statement_line(osv.osv):
528
_name = "asgard.ledger.export.statement.line"
529
_description = "Export Statement reconcile"
533
'name': fields.char('Date', size=64, translate=True, required=True, readonly=True),
534
'ales_id': fields.many2one('asgard.ledger.export.statement', 'Asgard Statement', required=True, ondelete='cascade', select=True),
535
'move_line_id': fields.many2one('account.move.line', 'Entry',
536
select=True, required=True,
537
help="Entry selected for ..."),
538
'date_created': fields.related('move_line_id', 'date_created', type='date', string='Date Created Entry', store=True),
539
'move_id': fields.related('move_line_id', 'move_id', type='many2one', relation='account.move', string='Entry', store=True),
540
'period_id': fields.related('move_line_id', 'period_id', type='many2one', relation='account.period', string='Period', store=True),
541
'journal_id': fields.related('move_line_id', 'journal_id', type='many2one', relation='account.journal', string='Journal', store=True),
542
'credit': fields.related('move_line_id', 'credit', type='float', string='Credit', store=True),
543
'debit': fields.related('move_line_id', 'debit', type='float', string='Debit', store=True),
544
'account_id': fields.related('move_line_id', 'account_id', type='many2one', relation='account.account', string='Account', store=True),
545
'text_line': fields.text('Line exported', readonly=True,
546
help="Value of the line when it's exported in file (From format field)"),
547
'company_id': fields.many2one('res.company', 'Company', required=True),
551
'name': lambda *a: time.strftime('%Y/%m/%d-%H:%M:%S'),
552
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'account.account', context=c),
556
asgard_ledger_export_statement_line()