~unifield-team/unifield-wm/us-826

« back to all changes in this revision

Viewing changes to account_override/finance_export.py

  • Committer: jf
  • Date: 2011-03-23 13:23:55 UTC
  • Revision ID: jf@tempo4-20110323132355-agyf1soy7m5ewatr
Initial Import

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
# -*- coding: utf-8 -*-
3
 
##############################################################################
4
 
#
5
 
#    OpenERP, Open Source Management Solution
6
 
#    Copyright (C) 2013 TeMPO Consulting, MSF. All Rights Reserved
7
 
#    Developer: Olivier DOSSMANN
8
 
#
9
 
#    This program is free software: you can redistribute it and/or modify
10
 
#    it under the terms of the GNU Affero General Public License as
11
 
#    published by the Free Software Foundation, either version 3 of the
12
 
#    License, or (at your option) any later version.
13
 
#
14
 
#    This program is distributed in the hope that it will be useful,
15
 
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
#    GNU Affero General Public License for more details.
18
 
#
19
 
#    You should have received a copy of the GNU Affero General Public License
20
 
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 
#
22
 
##############################################################################
23
 
 
24
 
from osv import osv
25
 
from tools.translate import _
26
 
import csv
27
 
from cStringIO import StringIO
28
 
import pooler
29
 
import zipfile
30
 
from tempfile import NamedTemporaryFile
31
 
import os
32
 
 
33
 
class finance_archive():
34
 
    """
35
 
    SQLREQUESTS DICTIONNARY
36
 
     - key: name of the SQL request
37
 
     - value: the SQL request to use
38
 
 
39
 
    PROCESS REQUESTS LIST: list of dict containing info to process some SQL requests
40
 
    Dict:
41
 
     - [optional] headers: list of headers that should appears in the CSV file
42
 
     - filename: the name of the result filename in the future ZIP file
43
 
     - key: the name of the key in SQLREQUESTS DICTIONNARY to have the right SQL request
44
 
     - [optional] query_params: data to use to complete SQL requests
45
 
     - [optional] function: name of the function to postprocess data (example: to change selection field into a human readable text)
46
 
     - [optional] fnct_params: params that would used on the given function
47
 
     - [optional] delete_columns: list of columns to delete before writing files into result
48
 
     - [optional] id (need 'object'): number of the column that contains the ID of the element. Column number begin from 0. Note that you should adapt your SQL request to add the ID of lines.
49
 
     - [optional] object (need 'id'): name of the object in the system. For an example: 'account.bank.statement'.
50
 
    TIP & TRICKS:
51
 
     + More than 1 request in 1 file: just use same filename for each request you want to be in the same file.
52
 
     + If you cannot do a SQL request to create the content of the file, do a simple request (with key) and add a postprocess function that returns the result you want
53
 
     + Do not repeat headers if you use the same filename for more than 1 request. This avoid having multiple lines as headers.
54
 
    """
55
 
 
56
 
    def __init__(self, sql, process):
57
 
        self.sqlrequests = sql
58
 
        self.processrequests = process
59
 
 
60
 
    def line_to_utf8(self, line):
61
 
        """
62
 
        Change all elements of this line to UTF-8
63
 
        """
64
 
        newline = []
65
 
        if not line:
66
 
            return []
67
 
        for element in line:
68
 
            if type(element) == unicode:
69
 
                newline.append(element.encode('utf-8'))
70
 
            else:
71
 
                newline.append(element)
72
 
        return newline
73
 
 
74
 
    def delete_x_column(self, line, columns=[]):
75
 
        """
76
 
        Take numbers in 'columns' list and delete them from given line.
77
 
        Begin from 0.
78
 
        """
79
 
        if not line:
80
 
            return []
81
 
        if not columns:
82
 
            return line
83
 
        # Create a list of all elements from line except those given in columns
84
 
        newline = []
85
 
        for element in sorted([x for x in xrange(len(line)) if x not in columns]):
86
 
            newline.append(line[element])
87
 
        return newline
88
 
 
89
 
    def get_selection(self, cr, model, field):
90
 
        """
91
 
        Return a list of all selection from a field in a given model.
92
 
        """
93
 
        pool = pooler.get_pool(cr.dbname)
94
 
        data = pool.get(model).fields_get(cr, 1, [field])
95
 
        return dict(data[field]['selection'])
96
 
 
97
 
    def postprocess_selection_columns(self, cr, uid, data, changes, column_deletion=False):
98
 
        """
99
 
        This method takes each line from data and change some columns regarding "changes" variable.
100
 
        'changes' should be a list containing some tuples. A tuple is composed of:
101
 
         - a model (example: res.partner)
102
 
         - the selection field in which retrieve all real values (example: partner_type)
103
 
         - the column number in the data lines from which you want to change the value (begin from 0)
104
 
        """
105
 
        # Checks
106
 
        if not changes:
107
 
            return data
108
 
        # Prepare some values
109
 
        new_data = []
110
 
        # Fetch selections
111
 
        changes_values = {}
112
 
        for change in changes:
113
 
            model = change[0]
114
 
            field = change[1]
115
 
            changes_values[change] = self.get_selection(cr, model, field)
116
 
        # Browse each line to replace partner type by it's human readable value (selection)
117
 
        # partner_type is the 3rd column
118
 
        for line in data:
119
 
            tmp_line = list(line)
120
 
            # Delete some columns if needed
121
 
            if column_deletion:
122
 
                tmp_line = self.delete_x_column(list(line), column_deletion)
123
 
            for change in changes:
124
 
                column = change[2]
125
 
                # use line value to search into changes_values[change] (the list of selection) the right value
126
 
                tmp_line[column] = changes_values[change][tmp_line[column]]
127
 
            new_data.append(self.line_to_utf8(tmp_line))
128
 
        return new_data
129
 
 
130
 
    def archive(self, cr, uid):
131
 
        """
132
 
        Create an archive with sqlrequests params and processrequests params.
133
 
        """
134
 
        # open buffer for result zipfile
135
 
        zip_buffer = StringIO()
136
 
        # Prepare some values
137
 
        pool = pooler.get_pool(cr.dbname)
138
 
 
139
 
        # List is composed of a tuple containing:
140
 
        # - filename
141
 
        # - key of sqlrequests dict to fetch its SQL request
142
 
        files = {}
143
 
        for fileparams in self.processrequests:
144
 
            if not fileparams.get('filename', False):
145
 
                raise osv.except_osv(_('Error'), _('Filename param is missing!'))
146
 
            if not fileparams.get('key', False):
147
 
                raise osv.except_osv(_('Error'), _('Key param is missing!'))
148
 
            # temporary file (process filename to display datetime data instead of %(year)s chars)
149
 
            filename = pool.get('ir.sequence')._process(cr, uid, fileparams['filename'] or '') or fileparams['filename']
150
 
            if filename not in files:
151
 
                tmp_file = NamedTemporaryFile('w+b', delete=False)
152
 
            else:
153
 
                tmp_file = files[filename]
154
 
 
155
 
            # fetch data with given sql query
156
 
            sql = self.sqlrequests[fileparams['key']]
157
 
            if fileparams.get('query_params', False):
158
 
                cr.execute(sql, fileparams['query_params'])
159
 
            else:
160
 
                cr.execute(sql)
161
 
            sqlres = cr.fetchall()
162
 
            # Fetch ID column and mark lines as exported
163
 
            if fileparams.get('id', None) != None:
164
 
                if not fileparams.get('object', False):
165
 
                    raise osv.except_osv(_('Error'), _('object param is missing to use ID one.'))
166
 
                # prepare needed values
167
 
                object_name = fileparams['object']
168
 
                pool = pooler.get_pool(cr.dbname)
169
 
                tablename = pool.get(object_name)._table
170
 
                if not tablename:
171
 
                    raise osv.except_osv(_('Error'), _("Table name not found for the given object: %s") % (fileparams['object'],))
172
 
                key_column_number = fileparams['id']
173
 
                # get ids from previous request
174
 
                ids = [x and x[key_column_number] or 0 for x in sqlres]
175
 
                # mark lines as exported
176
 
                if ids:
177
 
                    update_request = 'UPDATE ' + tablename + ' SET exported=\'t\' WHERE id in %s'
178
 
                    try:
179
 
                        cr.execute(update_request, (tuple(ids),))
180
 
                    except Exception, e:
181
 
                        raise osv.except_osv(_('Error'), _('An error occured: %s') % (e.message and e.message or '',))
182
 
            without_headers = []
183
 
            # Check if a function is given. If yes, use it.
184
 
            # If not, transform lines into UTF-8. Note that postprocess method should transform lines into UTF-8 ones.
185
 
            if fileparams.get('function', False):
186
 
                fnct = getattr(self, fileparams['function'], False)
187
 
                delete_columns = fileparams.get('delete_columns', False)
188
 
                # If the function has some params, use them.
189
 
                if fnct and fileparams.get('fnct_params', False):
190
 
                    without_headers = fnct(cr, uid, sqlres, fileparams['fnct_params'], column_deletion=delete_columns)
191
 
                elif fnct:
192
 
                    without_headers = fnct(cr, uid, sqlres, column_deletion=delete_columns)
193
 
            else:
194
 
                # Change to UTF-8 all unicode elements
195
 
                for line in sqlres:
196
 
                    # Delete useless columns if needed
197
 
                    if fileparams.get('delete_columns', False):
198
 
                        line = self.delete_x_column(line, fileparams['delete_columns'])
199
 
                    without_headers.append(self.line_to_utf8(line))
200
 
            result = without_headers
201
 
            if fileparams.get('headers', False):
202
 
                headers = [fileparams['headers']]
203
 
                for line in result:
204
 
                    headers.append(line)
205
 
                result = headers
206
 
            # Write result in a CSV writer then close it.
207
 
            writer = csv.writer(tmp_file, quoting=csv.QUOTE_ALL)
208
 
            writer.writerows(result)
209
 
            # Only add a link to the temporary file if not in "files" dict
210
 
            if filename not in files:
211
 
                files[filename] = tmp_file
212
 
 
213
 
        # WRITE RESULT INTO AN ARCHIVE
214
 
        # Create a ZIP file
215
 
        out_zipfile = zipfile.ZipFile(zip_buffer, "w")
216
 
        for filename in files:
217
 
            tmpfile = files[filename]
218
 
            # close temporary file
219
 
            tmpfile.close()
220
 
            # write content into zipfile
221
 
            out_zipfile.write(tmpfile.name, filename, zipfile.ZIP_DEFLATED)
222
 
            # unlink temporary file
223
 
            os.unlink(tmpfile.name)
224
 
        # close zip
225
 
        out_zipfile.close()
226
 
        out = zip_buffer.getvalue()
227
 
        # Return result
228
 
        return (out, 'zip')
229
 
 
230
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: