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

« back to all changes in this revision

Viewing changes to account_tools/finance_export.py

UF-385 [ADD] Added consumption_calculation module

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
 
        pool = pooler.get_pool(cr.dbname)
110
 
        new_data = []
111
 
        # Fetch selections
112
 
        changes_values = {}
113
 
        for change in changes:
114
 
            model = change[0]
115
 
            field = change[1]
116
 
            changes_values[change] = self.get_selection(cr, model, field)
117
 
        # Browse each line to replace partner type by it's human readable value (selection)
118
 
        # partner_type is the 3rd column
119
 
        for line in data:
120
 
            tmp_line = list(line)
121
 
            # Delete some columns if needed
122
 
            if column_deletion:
123
 
                tmp_line = self.delete_x_column(list(line), column_deletion)
124
 
            for change in changes:
125
 
                column = change[2]
126
 
                # use line value to search into changes_values[change] (the list of selection) the right value
127
 
                tmp_line[column] = changes_values[change][tmp_line[column]]
128
 
            new_data.append(self.line_to_utf8(tmp_line))
129
 
        return new_data
130
 
 
131
 
    def archive(self, cr, uid):
132
 
        """
133
 
        Create an archive with sqlrequests params and processrequests params.
134
 
        """
135
 
        # open buffer for result zipfile
136
 
        zip_buffer = StringIO()
137
 
        # Prepare some values
138
 
        pool = pooler.get_pool(cr.dbname)
139
 
 
140
 
        # List is composed of a tuple containing:
141
 
        # - filename
142
 
        # - key of sqlrequests dict to fetch its SQL request
143
 
        files = {}
144
 
        for fileparams in self.processrequests:
145
 
            if not fileparams.get('filename', False):
146
 
                raise osv.except_osv(_('Error'), _('Filename param is missing!'))
147
 
            if not fileparams.get('key', False):
148
 
                raise osv.except_osv(_('Error'), _('Key param is missing!'))
149
 
            # temporary file (process filename to display datetime data instead of %(year)s chars)
150
 
            filename = pool.get('ir.sequence')._process(cr, uid, fileparams['filename'] or '') or fileparams['filename']
151
 
            if filename not in files:
152
 
                tmp_file = NamedTemporaryFile('w+b', delete=False)
153
 
            else:
154
 
                tmp_file = files[filename]
155
 
 
156
 
            # fetch data with given sql query
157
 
            sql = self.sqlrequests[fileparams['key']]
158
 
            if fileparams.get('query_params', False):
159
 
                cr.execute(sql, fileparams['query_params'])
160
 
            else:
161
 
                cr.execute(sql)
162
 
            sqlres = cr.fetchall()
163
 
            # Fetch ID column and mark lines as exported
164
 
            if fileparams.get('id', None) != None:
165
 
                if not fileparams.get('object', False):
166
 
                    raise osv.except_osv(_('Error'), _('object param is missing to use ID one.'))
167
 
                # prepare needed values
168
 
                object_name = fileparams['object']
169
 
                pool = pooler.get_pool(cr.dbname)
170
 
                tablename = pool.get(object_name)._table
171
 
                if not tablename:
172
 
                    raise osv.except_osv(_('Error'), _("Table name not found for the given object: %s") % (fileparams['object'],))
173
 
                key_column_number = fileparams['id']
174
 
                # get ids from previous request
175
 
                ids = [x and x[key_column_number] or 0 for x in sqlres]
176
 
                # mark lines as exported
177
 
                if ids:
178
 
                    update_request = 'UPDATE ' + tablename + ' SET exported=\'t\' WHERE id in %s'
179
 
                    try:
180
 
                        cr.execute(update_request, (tuple(ids),))
181
 
                    except Exception, e:
182
 
                        raise osv.except_osv(_('Error'), _('An error occured: %s') % (e.message and e.message or '',))
183
 
            without_headers = []
184
 
            # Check if a function is given. If yes, use it.
185
 
            # If not, transform lines into UTF-8. Note that postprocess method should transform lines into UTF-8 ones.
186
 
            if fileparams.get('function', False):
187
 
                fnct = getattr(self, fileparams['function'], False)
188
 
                delete_columns = fileparams.get('delete_columns', False)
189
 
                # If the function has some params, use them.
190
 
                if fnct and fileparams.get('fnct_params', False):
191
 
                    without_headers = fnct(cr, uid, sqlres, fileparams['fnct_params'], column_deletion=delete_columns)
192
 
                elif fnct:
193
 
                    without_headers = fnct(cr, uid, sqlres, column_deletion=delete_columns)
194
 
            else:
195
 
                # Change to UTF-8 all unicode elements
196
 
                for line in sqlres:
197
 
                    # Delete useless columns if needed
198
 
                    if fileparams.get('delete_columns', False):
199
 
                        line = self.delete_x_column(line, fileparams['delete_columns'])
200
 
                    without_headers.append(self.line_to_utf8(line))
201
 
            result = without_headers
202
 
            if fileparams.get('headers', False):
203
 
                headers = [fileparams['headers']]
204
 
                for line in result:
205
 
                    headers.append(line)
206
 
                result = headers
207
 
            # Write result in a CSV writer then close it.
208
 
            writer = csv.writer(tmp_file, quoting=csv.QUOTE_ALL)
209
 
            writer.writerows(result)
210
 
            # Only add a link to the temporary file if not in "files" dict
211
 
            if filename not in files:
212
 
                files[filename] = tmp_file
213
 
 
214
 
        # WRITE RESULT INTO AN ARCHIVE
215
 
        # Create a ZIP file
216
 
        out_zipfile = zipfile.ZipFile(zip_buffer, "w")
217
 
        for filename in files:
218
 
            tmpfile = files[filename]
219
 
            # close temporary file
220
 
            tmpfile.close()
221
 
            # write content into zipfile
222
 
            out_zipfile.write(tmpfile.name, filename, zipfile.ZIP_DEFLATED)
223
 
            # unlink temporary file
224
 
            os.unlink(tmpfile.name)
225
 
        # close zip
226
 
        out_zipfile.close()
227
 
        out = zip_buffer.getvalue()
228
 
        # Return result
229
 
        return (out, 'zip')
230
 
 
231
 
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: