[pep8] - account_export_csv

pull/6/head
laetitia.gangloff@acsone.eu 2014-08-07 11:17:05 +02:00
parent 13a707dc93
commit 35ca28d965
4 changed files with 161 additions and 114 deletions

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author Joel Grand-Guillaume and Vincent Renaville Copyright 2013 Camptocamp SA # Author Joel Grand-Guillaume and Vincent Renaville Copyright 2013
# Camptocamp SA
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author Joel Grand-Guillaume and Vincent Renaville Copyright 2013 Camptocamp SA # Author Joel Grand-Guillaume and Vincent Renaville
# Copyright 2013 Camptocamp SA
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -19,15 +20,16 @@
############################################################################## ##############################################################################
{ {
'name' : 'Account Export CSV', 'name': 'Account Export CSV',
'version' : '1.1', 'version': '1.1',
'depends' : [ 'depends': [
'account', 'account',
], ],
'author' : 'Camptocamp', 'author': 'Camptocamp',
'description': """ 'description': """
Add a wizard that allow you to export a csv file based on accounting journal entries Add a wizard that allow you to export a csv file based on accounting
journal entries
- Trial Balance - Trial Balance
- Analytic Balance (with accounts) - Analytic Balance (with accounts)
@ -35,13 +37,14 @@
You can filter by period You can filter by period
TODO: rearange wizard view with only one button to generate file plus define a selection list to select report type TODO: rearange wizard view with only one button to generate file plus
define a selection list to select report type
""", """,
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'data': [ 'data': [
'wizard/account_export_csv_view.xml', 'wizard/account_export_csv_view.xml',
'menu.xml', 'menu.xml',
], ],
'installable': True, 'installable': True,
'active': False, 'active': False,
} }

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author Joel Grand-Guillaume and Vincent Renaville Copyright 2013 Camptocamp SA # Author Joel Grand-Guillaume and Vincent Renaville Copyright 2013
# Camptocamp SA
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as

View File

@ -1,8 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author Joel Grand-Guillaume and Vincent Renaville Copyright 2013 Camptocamp SA # Author Joel Grand-Guillaume and Vincent Renaville Copyright 2013
# CSV data formating inspired from http://docs.python.org/2.7/library/csv.html?highlight=csv#examples # Camptocamp SA
# CSV data formating inspired from
# http://docs.python.org/2.7/library/csv.html?highlight=csv#examples
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
@ -20,7 +22,6 @@
############################################################################## ##############################################################################
import itertools import itertools
import time
import tempfile import tempfile
import StringIO import StringIO
import cStringIO import cStringIO
@ -34,6 +35,7 @@ from openerp.tools.translate import _
class AccountUnicodeWriter(object): class AccountUnicodeWriter(object):
""" """
A CSV writer which will write rows to CSV file "f", A CSV writer which will write rows to CSV file "f",
which is encoded in the given encoding. which is encoded in the given encoding.
@ -48,10 +50,11 @@ class AccountUnicodeWriter(object):
self.encoder = codecs.getincrementalencoder(encoding)() self.encoder = codecs.getincrementalencoder(encoding)()
def writerow(self, row): def writerow(self, row):
#we ensure that we do not try to encode none or bool # we ensure that we do not try to encode none or bool
row = (x or u'' for x in row) row = (x or u'' for x in row)
encoded_row = [c.encode("utf-8") if isinstance(c, unicode) else c for c in row] encoded_row = [
c.encode("utf-8") if isinstance(c, unicode) else c for c in row]
self.writer.writerow(encoded_row) self.writer.writerow(encoded_row)
# Fetch UTF-8 output from the queue ... # Fetch UTF-8 output from the queue ...
@ -68,32 +71,47 @@ class AccountUnicodeWriter(object):
for row in rows: for row in rows:
self.writerow(row) self.writerow(row)
class AccountCSVExport(orm.TransientModel): class AccountCSVExport(orm.TransientModel):
_name = 'account.csv.export' _name = 'account.csv.export'
_description = 'Export Accounting' _description = 'Export Accounting'
_columns = { _columns = {
'data': fields.binary('CSV',readonly=True), 'data': fields.binary('CSV', readonly=True),
'company_id': fields.many2one('res.company', 'Company', invisible=True), 'company_id': fields.many2one('res.company', 'Company',
'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscalyear', required=True), invisible=True),
'periods': fields.many2many('account.period','rel_wizard_period','wizard_id','period_id','Periods',help='All periods in the fiscal year if empty'), 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscalyear',
'journal_ids': fields.many2many('account.journal','rel_wizard_journal','wizard_id','journal_id','Journals', help='If empty, use all journals, only used for journal entries'), required=True),
'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscalyear', required=True), 'periods': fields.many2many(
'account.period', 'rel_wizard_period',
'wizard_id', 'period_id', 'Periods',
help='All periods in the fiscal year if empty'),
'journal_ids': fields.many2many(
'account.journal',
'rel_wizard_journal',
'wizard_id',
'journal_id',
'Journals',
help='If empty, use all journals, only used for journal entries'),
'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscalyear',
required=True),
'export_filename': fields.char('Export CSV Filename', size=128), 'export_filename': fields.char('Export CSV Filename', size=128),
} }
def _get_company_default(self, cr, uid, context=None): def _get_company_default(self, cr, uid, context=None):
comp_obj = self.pool['res.company'] comp_obj = self.pool['res.company']
return comp_obj._company_default_get(cr, uid, 'account.fiscalyear', context=context) return comp_obj._company_default_get(cr, uid, 'account.fiscalyear',
context=context)
def _get_fiscalyear_default(self, cr, uid, context=None): def _get_fiscalyear_default(self, cr, uid, context=None):
fiscalyear_obj = self.pool['account.fiscalyear'] fiscalyear_obj = self.pool['account.fiscalyear']
context['company_id'] = self._get_company_default(cr, uid, context) context['company_id'] = self._get_company_default(cr, uid, context)
return fiscalyear_obj.find(cr,uid,dt=None,exception=True, context=context) return fiscalyear_obj.find(cr, uid, dt=None, exception=True,
context=context)
_defaults = {'company_id': _get_company_default, _defaults = {'company_id': _get_company_default,
'fiscalyear_id' : _get_fiscalyear_default, 'fiscalyear_id': _get_fiscalyear_default,
'export_filename' : 'account_export.csv'} 'export_filename': 'account_export.csv'}
def action_manual_export_account(self, cr, uid, ids, context=None): def action_manual_export_account(self, cr, uid, ids, context=None):
this = self.browse(cr, uid, ids)[0] this = self.browse(cr, uid, ids)[0]
@ -118,7 +136,6 @@ class AccountCSVExport(orm.TransientModel):
'target': 'new', 'target': 'new',
} }
def _get_header_account(self, cr, uid, ids, context=None): def _get_header_account(self, cr, uid, ids, context=None):
return [_(u'CODE'), return [_(u'CODE'),
_(u'NAME'), _(u'NAME'),
@ -128,24 +145,27 @@ class AccountCSVExport(orm.TransientModel):
] ]
def _get_rows_account(self, cr, uid, ids, def _get_rows_account(self, cr, uid, ids,
fiscalyear_id, fiscalyear_id,
period_range_ids, period_range_ids,
journal_ids, journal_ids,
context=None): context=None):
""" """
Return list to generate rows of the CSV file Return list to generate rows of the CSV file
""" """
cr.execute(""" cr.execute("""
select ac.code,ac.name, select ac.code,ac.name,
sum(debit) as sum_debit,sum(credit) as sum_credit,sum(debit) - sum(credit) as balance sum(debit) as sum_debit,
from account_move_line as aml,account_account as ac sum(credit) as sum_credit,
where aml.account_id = ac.id sum(debit) - sum(credit) as balance
and period_id in %(period_ids)s from account_move_line as aml,account_account as ac
group by ac.id,ac.code,ac.name where aml.account_id = ac.id
order by ac.code and period_id in %(period_ids)s
group by ac.id,ac.code,ac.name
order by ac.code
""", """,
{'fiscalyear_id': fiscalyear_id, 'period_ids':tuple(period_range_ids)} {'fiscalyear_id': fiscalyear_id,
) 'period_ids': tuple(period_range_ids)}
)
res = cr.fetchall() res = cr.fetchall()
rows = [] rows = []
@ -155,7 +175,7 @@ class AccountCSVExport(orm.TransientModel):
def action_manual_export_analytic(self, cr, uid, ids, context=None): def action_manual_export_analytic(self, cr, uid, ids, context=None):
this = self.browse(cr, uid, ids)[0] this = self.browse(cr, uid, ids)[0]
rows = self.get_data(cr, uid, ids,"analytic", context) rows = self.get_data(cr, uid, ids, "analytic", context)
file_data = StringIO.StringIO() file_data = StringIO.StringIO()
try: try:
writer = AccountUnicodeWriter(file_data) writer = AccountUnicodeWriter(file_data)
@ -187,15 +207,19 @@ class AccountCSVExport(orm.TransientModel):
] ]
def _get_rows_analytic(self, cr, uid, ids, def _get_rows_analytic(self, cr, uid, ids,
fiscalyear_id, fiscalyear_id,
period_range_ids, period_range_ids,
journal_ids, journal_ids,
context=None): context=None):
""" """
Return list to generate rows of the CSV file Return list to generate rows of the CSV file
""" """
cr.execute(""" select aac.code as analytic_code,aac.name as analytic_name,ac.code,ac.name, cr.execute(""" select aac.code as analytic_code,
sum(debit) as sum_debit,sum(credit) as sum_credit,sum(debit) - sum(credit) as balance aac.name as analytic_name,
ac.code,ac.name,
sum(debit) as sum_debit,
sum(credit) as sum_credit,
sum(debit) - sum(credit) as balance
from account_move_line from account_move_line
left outer join account_analytic_account as aac left outer join account_analytic_account as aac
on (account_move_line.analytic_account_id = aac.id) on (account_move_line.analytic_account_id = aac.id)
@ -205,8 +229,9 @@ class AccountCSVExport(orm.TransientModel):
group by aac.id,aac.code,aac.name,ac.id,ac.code,ac.name group by aac.id,aac.code,aac.name,ac.id,ac.code,ac.name
order by aac.code order by aac.code
""", """,
{'fiscalyear_id': fiscalyear_id, 'period_ids':tuple(period_range_ids)} {'fiscalyear_id': fiscalyear_id,
) 'period_ids': tuple(period_range_ids)}
)
res = cr.fetchall() res = cr.fetchall()
rows = [] rows = []
@ -214,14 +239,15 @@ class AccountCSVExport(orm.TransientModel):
rows.append(list(line)) rows.append(list(line))
return rows return rows
def action_manual_export_journal_entries(self, cr, uid, ids, context=None): def action_manual_export_journal_entries(self, cr, uid, ids, context=None):
""" """
Here we use TemporaryFile to avoid full filling the OpenERP worker Memory Here we use TemporaryFile to avoid full filling the OpenERP worker
We also write the data to the wizard with SQL query as write seams to use Memory
too much memory as well. We also write the data to the wizard with SQL query as write seams
to use too much memory as well.
Those improvements permitted to improve the export from a 100k line to 200k lines Those improvements permitted to improve the export from a 100k line to
200k lines
with default `limit_memory_hard = 805306368` (768MB) with more lines, with default `limit_memory_hard = 805306368` (768MB) with more lines,
you might encounter a MemoryError when trying to download the file even you might encounter a MemoryError when trying to download the file even
if it has been generated. if it has been generated.
@ -241,7 +267,10 @@ class AccountCSVExport(orm.TransientModel):
file_data.seek(0) file_data.seek(0)
base64.encode(file_data, base64_data) base64.encode(file_data, base64_data)
base64_data.seek(0) base64_data.seek(0)
cr.execute("""UPDATE account_csv_export SET data = %s WHERE id = %s""", (base64_data.read(), ids[0]) ) cr.execute("""
UPDATE account_csv_export
SET data = %s
WHERE id = %s""", (base64_data.read(), ids[0]))
return { return {
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
'res_model': 'account.csv.export', 'res_model': 'account.csv.export',
@ -252,44 +281,42 @@ class AccountCSVExport(orm.TransientModel):
'target': 'new', 'target': 'new',
} }
def _get_header_journal_entries(self, cr, uid, ids, context=None): def _get_header_journal_entries(self, cr, uid, ids, context=None):
return [ return [
# Standard Sage export fields # Standard Sage export fields
_(u'DATE'), _(u'DATE'),
_(u'JOURNAL CODE'), _(u'JOURNAL CODE'),
_(u'ACCOUNT CODE'), _(u'ACCOUNT CODE'),
_(u'PARTNER NAME'), _(u'PARTNER NAME'),
_(u'REF'), _(u'REF'),
_(u'DESCRIPTION'), _(u'DESCRIPTION'),
_(u'DEBIT'), _(u'DEBIT'),
_(u'CREDIT'), _(u'CREDIT'),
_(u'FULL RECONCILE'), _(u'FULL RECONCILE'),
_(u'PARTIAL RECONCILE'), _(u'PARTIAL RECONCILE'),
_(u'ANALYTIC ACCOUNT CODE'), _(u'ANALYTIC ACCOUNT CODE'),
# Other fields
_(u'ENTRY NUMBER'),
_(u'ACCOUNT NAME'),
_(u'BALANCE'),
_(u'AMOUNT CURRENCY'),
_(u'CURRENCY'),
_(u'ANALYTIC ACCOUNT NAME'),
_(u'JOURNAL'),
_(u'MONTH'),
_(u'FISCAL YEAR'),
_(u'TAX CODE CODE'),
_(u'TAX CODE NAME'),
_(u'TAX AMOUNT'),
]
# Other fields
_(u'ENTRY NUMBER'),
_(u'ACCOUNT NAME'),
_(u'BALANCE'),
_(u'AMOUNT CURRENCY'),
_(u'CURRENCY'),
_(u'ANALYTIC ACCOUNT NAME'),
_(u'JOURNAL'),
_(u'MONTH'),
_(u'FISCAL YEAR'),
_(u'TAX CODE CODE'),
_(u'TAX CODE NAME'),
_(u'TAX AMOUNT'),
]
def _get_rows_journal_entries(self, cr, uid, ids, def _get_rows_journal_entries(self, cr, uid, ids,
fiscalyear_id, fiscalyear_id,
period_range_ids, period_range_ids,
journal_ids, journal_ids,
context=None): context=None):
""" """
Create a generator of rows of the CSV file Create a generator of rows of the CSV file
""" """
@ -321,52 +348,67 @@ class AccountCSVExport(orm.TransientModel):
account_move_line.tax_amount AS aml_tax_amount account_move_line.tax_amount AS aml_tax_amount
FROM FROM
public.account_move_line public.account_move_line
JOIN account_account on (account_account.id=account_move_line.account_id) JOIN account_account on
JOIN account_period on (account_period.id=account_move_line.period_id) (account_account.id=account_move_line.account_id)
JOIN account_fiscalyear on (account_fiscalyear.id=account_period.fiscalyear_id) JOIN account_period on
JOIN account_journal on (account_journal.id = account_move_line.journal_id) (account_period.id=account_move_line.period_id)
LEFT JOIN res_currency on (res_currency.id=account_move_line.currency_id) JOIN account_fiscalyear on
LEFT JOIN account_move_reconcile on (account_move_reconcile.id = account_move_line.reconcile_id) (account_fiscalyear.id=account_period.fiscalyear_id)
LEFT JOIN res_partner on (res_partner.id=account_move_line.partner_id) JOIN account_journal on
LEFT JOIN account_move on (account_move.id=account_move_line.move_id) (account_journal.id = account_move_line.journal_id)
LEFT JOIN account_tax on (account_tax.id=account_move_line.account_tax_id) LEFT JOIN res_currency on
LEFT JOIN account_tax_code on (account_tax_code.id=account_move_line.tax_code_id) (res_currency.id=account_move_line.currency_id)
LEFT JOIN account_analytic_account on (account_analytic_account.id=account_move_line.analytic_account_id) LEFT JOIN account_move_reconcile on
(account_move_reconcile.id = account_move_line.reconcile_id)
LEFT JOIN res_partner on
(res_partner.id=account_move_line.partner_id)
LEFT JOIN account_move on
(account_move.id=account_move_line.move_id)
LEFT JOIN account_tax on
(account_tax.id=account_move_line.account_tax_id)
LEFT JOIN account_tax_code on
(account_tax_code.id=account_move_line.tax_code_id)
LEFT JOIN account_analytic_account on
(account_analytic_account.id=account_move_line.analytic_account_id)
WHERE account_period.id IN %(period_ids)s WHERE account_period.id IN %(period_ids)s
AND account_journal.id IN %(journal_ids)s AND account_journal.id IN %(journal_ids)s
ORDER BY account_move_line.date ORDER BY account_move_line.date
""", """,
{'period_ids': tuple(period_range_ids), 'journal_ids': tuple(journal_ids)} {'period_ids': tuple(
) period_range_ids), 'journal_ids': tuple(journal_ids)}
)
while 1: while 1:
# http://initd.org/psycopg/docs/cursor.html#cursor.fetchmany # http://initd.org/psycopg/docs/cursor.html#cursor.fetchmany
# Set cursor.arraysize to minimize network round trips # Set cursor.arraysize to minimize network round trips
cr.arraysize=100 cr.arraysize = 100
rows = cr.fetchmany() rows = cr.fetchmany()
if not rows: if not rows:
break break
for row in rows: for row in rows:
yield row yield row
def get_data(self, cr, uid, ids,result_type,context=None): def get_data(self, cr, uid, ids, result_type, context=None):
get_header_func = getattr(self,("_get_header_%s"%(result_type)), None) get_header_func = getattr(
get_rows_func = getattr(self,("_get_rows_%s"%(result_type)), None) self, ("_get_header_%s" % (result_type)), None)
get_rows_func = getattr(self, ("_get_rows_%s" % (result_type)), None)
form = self.browse(cr, uid, ids[0], context=context) form = self.browse(cr, uid, ids[0], context=context)
fiscalyear_id = form.fiscalyear_id.id fiscalyear_id = form.fiscalyear_id.id
user_obj = self.pool.get('res.users')
if form.periods: if form.periods:
period_range_ids = [x.id for x in form.periods] period_range_ids = [x.id for x in form.periods]
else: else:
# If not period selected , we take all periods # If not period selected , we take all periods
p_obj = self.pool.get("account.period") p_obj = self.pool.get("account.period")
period_range_ids = p_obj.search(cr, uid, [('fiscalyear_id','=',fiscalyear_id)], context=context) period_range_ids = p_obj.search(
cr, uid, [('fiscalyear_id', '=', fiscalyear_id)],
context=context)
journal_ids = None journal_ids = None
if form.journal_ids: if form.journal_ids:
journal_ids = [x.id for x in form.journal_ids] journal_ids = [x.id for x in form.journal_ids]
else: else:
j_obj = self.pool.get("account.journal") j_obj = self.pool.get("account.journal")
journal_ids = j_obj.search(cr, uid, [], context=context) journal_ids = j_obj.search(cr, uid, [], context=context)
rows = itertools.chain((get_header_func(cr, uid, ids, context=context),), rows = itertools.chain((get_header_func(cr, uid, ids,
context=context),),
get_rows_func(cr, uid, ids, get_rows_func(cr, uid, ids,
fiscalyear_id, fiscalyear_id,
period_range_ids, period_range_ids,