[FIX] bad import
[odoo/odoo.git] / addons / account_analytic_analysis / account_analytic_analysis.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution   
5 #    Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22 import operator
23 from osv import osv, fields
24 from osv.orm import intersect
25 import tools.sql 
26 from tools.translate import _
27
28
29 class account_analytic_account(osv.osv):
30     _name = "account.analytic.account"
31     _inherit = "account.analytic.account"
32
33     def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context={}):
34         res = {}
35         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
36         if ids2:
37             acc_set = ",".join(map(str, ids2))
38             cr.execute("select account_analytic_line.account_id, sum(amount) \
39                     from account_analytic_line \
40                     join account_analytic_journal \
41                         on account_analytic_line.journal_id = account_analytic_journal.id  \
42                     where account_analytic_line.account_id IN (%s) \
43                         and account_analytic_journal.type = 'sale' \
44                     group by account_analytic_line.account_id" % acc_set)
45             for account_id, sum in cr.fetchall():
46                 res[account_id] = round(sum,2)
47         for obj_id in ids:
48             res.setdefault(obj_id, 0.0)
49             for child_id in self.search(cr, uid,
50                     [('parent_id', 'child_of', [obj_id])]):
51                 if child_id != obj_id:
52                     res[obj_id] += res.get(child_id, 0.0)
53         for id in ids:
54             res[id] = round(res.get(id, 0.0),2)
55         return res
56
57     def _ca_to_invoice_calc(self, cr, uid, ids, name, arg, context={}):
58         res = {}
59         res2 = {}
60         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
61         if ids2:
62             # Amount uninvoiced hours to invoice at sale price
63             acc_set = ",".join(map(str, ids2))
64             cr.execute("""SELECT account_analytic_account.id, \
65                         sum (product_template.list_price * \
66                             account_analytic_line.unit_amount * \
67                             ((100-hr_timesheet_invoice_factor.factor)/100)) \
68                             AS ca_to_invoice \
69                     FROM product_template \
70                     join product_product \
71                         on product_template.id = product_product.product_tmpl_id \
72                     JOIN account_analytic_line \
73                         on account_analytic_line.product_id = product_product.id \
74                     JOIN account_analytic_journal \
75                         on account_analytic_line.journal_id = account_analytic_journal.id \
76                     JOIN account_analytic_account \
77                         on account_analytic_account.id = account_analytic_line.account_id \
78                     JOIN hr_timesheet_invoice_factor \
79                         on hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \
80                     WHERE account_analytic_account.id IN (%s) \
81                         AND account_analytic_line.invoice_id is null \
82                         AND account_analytic_line.to_invoice IS NOT NULL \
83                         and account_analytic_journal.type in ('purchase','general') \
84                     GROUP BY account_analytic_account.id;"""%acc_set)
85             for account_id, sum in cr.fetchall():
86                 res[account_id] = round(sum,2)
87
88             # Expense amount and purchase invoice
89             #acc_set = ",".join(map(str, ids2))
90             #cr.execute ("select account_analytic_line.account_id, sum(amount) \
91             #        from account_analytic_line \
92             #        join account_analytic_journal \
93             #            on account_analytic_line.journal_id = account_analytic_journal.id \
94             #        where account_analytic_line.account_id IN (%s) \
95             #            and account_analytic_journal.type = 'purchase' \
96             #        GROUP BY account_analytic_line.account_id;"%acc_set)
97             #for account_id, sum in cr.fetchall():
98             #    res2[account_id] = round(sum,2)
99         for obj_id in ids:
100             res.setdefault(obj_id, 0.0)
101             res2.setdefault(obj_id, 0.0)
102             for child_id in self.search(cr, uid,
103                     [('parent_id', 'child_of', [obj_id])]):
104                 if child_id != obj_id:
105                     res[obj_id] += res.get(child_id, 0.0)
106                     res2[obj_id] += res2.get(child_id, 0.0)
107         # sum both result on account_id
108         for id in ids:
109             res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2)
110         return res
111
112     def _hours_qtt_non_invoiced_calc (self, cr, uid, ids, name, arg, context={}):
113         res = {}
114         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
115         if ids2:
116             acc_set = ",".join(map(str, ids2))
117             cr.execute("select account_analytic_line.account_id, sum(unit_amount) \
118                     from account_analytic_line \
119                     join account_analytic_journal \
120                         on account_analytic_line.journal_id = account_analytic_journal.id \
121                     where account_analytic_line.account_id IN (%s) \
122                         and account_analytic_journal.type='general' \
123                         and invoice_id is null \
124                         AND to_invoice IS NOT NULL \
125                     GROUP BY account_analytic_line.account_id;"%acc_set)
126             for account_id, sum in cr.fetchall():
127                 res[account_id] = round(sum,2)
128         for obj_id in ids:
129             res.setdefault(obj_id, 0.0)
130             for child_id in self.search(cr, uid,
131                     [('parent_id', 'child_of', [obj_id])]):
132                 if child_id != obj_id:
133                     res[obj_id] += res.get(child_id, 0.0)
134         for id in ids:
135             res[id] = round(res.get(id, 0.0),2)
136         return res
137
138     def _hours_quantity_calc(self, cr, uid, ids, name, arg, context={}):
139         res = {}
140         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
141         if ids2:
142             acc_set = ",".join(map(str, ids2))
143             cr.execute("select account_analytic_line.account_id,sum(unit_amount) \
144                     from account_analytic_line \
145                     join account_analytic_journal \
146                         on account_analytic_line.journal_id = account_analytic_journal.id \
147                     where account_analytic_line.account_id IN (%s) \
148                         and account_analytic_journal.type='general' \
149                     GROUP BY account_analytic_line.account_id"%acc_set)
150             for account_id, sum in cr.fetchall():
151                 res[account_id] = round(sum,2)
152         for obj_id in ids:
153             res.setdefault(obj_id, 0.0)
154             for child_id in self.search(cr, uid,
155                     [('parent_id', 'child_of', [obj_id])]):
156                 if child_id != obj_id:
157                     res[obj_id] += res.get(child_id, 0.0)
158         for id in ids:
159             res[id] = round(res.get(id, 0.0),2)
160         return res
161
162     def _total_cost_calc(self, cr, uid, ids, name, arg, context={}):
163         res = {}
164         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
165         if ids2:
166             acc_set = ",".join(map(str, ids2))
167             cr.execute("""select account_analytic_line.account_id,sum(amount) \
168                     from account_analytic_line \
169                     join account_analytic_journal \
170                         on account_analytic_line.journal_id = account_analytic_journal.id \
171                     where account_analytic_line.account_id IN (%s) \
172                         and amount<0 \
173                     GROUP BY account_analytic_line.account_id"""%acc_set)
174             for account_id, sum in cr.fetchall():
175                 res[account_id] = round(sum,2)
176         for obj_id in ids:
177             res.setdefault(obj_id, 0.0)
178             for child_id in self.search(cr, uid,
179                     [('parent_id', 'child_of', [obj_id])]):
180                 if child_id != obj_id:
181                     res[obj_id] += res.get(child_id, 0.0)
182         for id in ids:
183             res[id] = round(res.get(id, 0.0),2)
184         return res
185
186     def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}):
187         res = {}
188         res2 = {}
189         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
190         if ids2:
191             acc_set = ",".join(map(str, ids2))
192             cr.execute("""select account_analytic_line.account_id as account_id, \
193                         sum((account_analytic_line.unit_amount * pt.list_price) \
194                             - (account_analytic_line.unit_amount * pt.list_price \
195                                 * hr.factor)) as somme
196                     from account_analytic_line \
197                     left join account_analytic_journal \
198                         on (account_analytic_line.journal_id = account_analytic_journal.id) \
199                     join product_product pp \
200                         on (account_analytic_line.product_id = pp.id) \
201                     join product_template pt \
202                         on (pp.product_tmpl_id = pt.id) \
203                     join account_analytic_account a \
204                         on (a.id=account_analytic_line.account_id) \
205                     join hr_timesheet_invoice_factor hr \
206                         on (hr.id=a.to_invoice) \
207                 where account_analytic_line.account_id IN (%s) \
208                     and a.to_invoice IS NOT NULL \
209                     and account_analytic_journal.type in ('purchase','general')
210                 GROUP BY account_analytic_line.account_id"""%acc_set)
211             for account_id, sum in cr.fetchall():
212                 res2[account_id] = round(sum,2)
213
214         for obj_id in ids:
215             res.setdefault(obj_id, 0.0)
216             res2.setdefault(obj_id, 0.0)
217             for child_id in self.search(cr, uid,
218                     [('parent_id', 'child_of', [obj_id])]):
219                 if child_id != obj_id:
220                     res[obj_id] += res.get(child_id, 0.0)
221                     res[obj_id] += res2.get(child_id, 0.0)
222
223         # sum both result on account_id
224         for id in ids:
225             res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2)
226         return res
227
228     def _last_worked_date_calc (self, cr, uid, ids, name, arg, context={}):
229         res = {}
230         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
231         if ids2:
232             acc_set = ",".join(map(str, ids2))
233             cr.execute("select account_analytic_line.account_id, max(date) \
234                     from account_analytic_line \
235                     where account_id IN (%s) \
236                         and invoice_id is null \
237                     GROUP BY account_analytic_line.account_id" % acc_set)
238             for account_id, sum in cr.fetchall():
239                 res[account_id] = sum
240         for obj_id in ids:
241             res.setdefault(obj_id, '')
242             for child_id in self.search(cr, uid,
243                     [('parent_id', 'child_of', [obj_id])]):
244                 if res[obj_id] < res.get(child_id, ''):
245                     res[obj_id] = res.get(child_id, '')
246         for id in ids:
247             res[id] = res.get(id, '')
248         return res
249
250     def _last_invoice_date_calc (self, cr, uid, ids, name, arg, context={}):
251         res = {}
252         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
253         if ids2:
254             acc_set = ",".join(map(str, ids2))
255             cr.execute ("select account_analytic_line.account_id, \
256                         date(max(account_invoice.date_invoice)) \
257                     from account_analytic_line \
258                     join account_invoice \
259                         on account_analytic_line.invoice_id = account_invoice.id \
260                     where account_analytic_line.account_id IN (%s) \
261                         and account_analytic_line.invoice_id is not null \
262                     GROUP BY account_analytic_line.account_id"%acc_set)
263             for account_id, sum in cr.fetchall():
264                 res[account_id] = sum
265         for obj_id in ids:
266             res.setdefault(obj_id, '')
267             for child_id in self.search(cr, uid,
268                     [('parent_id', 'child_of', [obj_id])]):
269                 if res[obj_id] < res.get(child_id, ''):
270                     res[obj_id] = res.get(child_id, '')
271         for id in ids:
272             res[id] = res.get(id, '')
273         return res
274
275     def _last_worked_invoiced_date_calc (self, cr, uid, ids, name, arg, context={}):
276         res = {}
277         ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
278         if ids2:
279             acc_set = ",".join(map(str, ids2))
280             cr.execute("select account_analytic_line.account_id, max(date) \
281                     from account_analytic_line \
282                     where account_id IN (%s) \
283                         and invoice_id is not null \
284                     GROUP BY account_analytic_line.account_id;"%acc_set)
285             for account_id, sum in cr.fetchall():
286                 res[account_id] = sum
287         for obj_id in ids:
288             res.setdefault(obj_id, '')
289             for child_id in self.search(cr, uid,
290                     [('parent_id', 'child_of', [obj_id])]):
291                 if res[obj_id] < res.get(child_id, ''):
292                     res[obj_id] = res.get(child_id, '')
293         for id in ids:
294             res[id] = res.get(id, '')
295         return res
296
297     def _remaining_hours_calc(self, cr, uid, ids, name, arg, context={}):
298         res = {}
299         for account in self.browse(cr, uid, ids):
300             if account.quantity_max <> 0:
301                 res[account.id] = account.quantity_max - account.hours_quantity
302             else:
303                 res[account.id]=0.0
304         for id in ids:
305             res[id] = round(res.get(id, 0.0),2)
306         return res
307
308     def _hours_qtt_invoiced_calc(self, cr, uid, ids, name, arg, context={}):
309         res = {}
310         for account in self.browse(cr, uid, ids):
311             res[account.id] = account.hours_quantity - account.hours_qtt_non_invoiced
312             if res[account.id] < 0:
313                 res[account.id]=0.0
314         for id in ids:
315             res[id] = round(res.get(id, 0.0),2)
316         return res
317
318     def _revenue_per_hour_calc(self, cr, uid, ids, name, arg, context={}):
319         res = {}
320         for account in self.browse(cr, uid, ids):
321             if account.hours_qtt_invoiced == 0:
322                 res[account.id]=0.0
323             else:
324                 res[account.id] = account.ca_invoiced / account.hours_qtt_invoiced
325         for id in ids:
326             res[id] = round(res.get(id, 0.0),2)
327         return res
328
329     def _real_margin_rate_calc(self, cr, uid, ids, name, arg, context={}):
330         res = {}
331         for account in self.browse(cr, uid, ids):
332             if account.ca_invoiced == 0:
333                 res[account.id]=0.0
334             elif account.total_cost <> 0.0:
335                 res[account.id] = -(account.real_margin / account.total_cost) * 100
336             else:
337                 res[account.id] = 0.0
338         for id in ids:
339             res[id] = round(res.get(id, 0.0),2)
340         return res
341
342     def _remaining_ca_calc(self, cr, uid, ids, name, arg, context={}):
343         res = {}
344         for account in self.browse(cr, uid, ids):
345             if account.amount_max <> 0:
346                 res[account.id] = account.amount_max - account.ca_invoiced
347             else:
348                 res[account.id]=0.0
349         for id in ids:
350             res[id] = round(res.get(id, 0.0),2)
351         return res
352
353     def _real_margin_calc(self, cr, uid, ids, name, arg, context={}):
354         res = {}
355         for account in self.browse(cr, uid, ids):
356             res[account.id] = account.ca_invoiced + account.total_cost
357         for id in ids:
358             res[id] = round(res.get(id, 0.0),2)
359         return res
360
361     def _theorical_margin_calc(self, cr, uid, ids, name, arg, context={}):
362         res = {}
363         for account in self.browse(cr, uid, ids):
364             res[account.id] = account.ca_theorical + account.total_cost
365         for id in ids:
366             res[id] = round(res.get(id, 0.0),2)
367         return res
368
369     def _month(self, cr, uid, ids, name, arg, context=None):
370         res = {}
371         for id in ids:
372             ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])])
373             cr.execute('SELECT DISTINCT(month_id) FROM account_analytic_analysis_summary_month ' \
374                     'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \
375                         'AND unit_amount <> 0.0')
376             res[id] = [int(id * 1000000 + int(x[0])) for x in cr.fetchall()]
377         return res
378
379     def _user(self, cr, uid, ids, name, arg, context=None):
380         res = {}
381         cr.execute('SELECT MAX(id) FROM res_users')
382         max_user = cr.fetchone()[0]
383         for id in ids:
384             ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])])
385             cr.execute('SELECT DISTINCT("user") FROM account_analytic_analysis_summary_user ' \
386                     'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \
387                         'AND unit_amount <> 0.0')
388             res[id] = [int((id * max_user) + x[0]) for x in cr.fetchall()]
389         return res
390
391     _columns ={
392         'ca_invoiced': fields.function(_ca_invoiced_calc, method=True, type='float', string='Invoiced Amount', help="Total customer invoiced amount for this account."),
393         'total_cost': fields.function(_total_cost_calc, method=True, type='float', string='Total Costs', help="Total of costs for this account. It includes real costs (from invoices) and indirect costs, like time spent on timesheets."),
394         'ca_to_invoice': fields.function(_ca_to_invoice_calc, method=True, type='float', string='Uninvoiced Amount', help="If invoice from analytic account, the remaining amount you can invoice to the customer based on the total costs."),
395         'ca_theorical': fields.function(_ca_theorical_calc, method=True, type='float', string='Theorical Revenue', help="Based on the costs you had on the project, what would have been the revenue if all these costs have been invoiced at the normal sale price provided by the pricelist."),
396         'hours_quantity': fields.function(_hours_quantity_calc, method=True, type='float', string='Hours Tot', help="Number of hours you spent on the analytic account (from timesheet). It computes on all journal of type 'general'."),
397         'last_invoice_date': fields.function(_last_invoice_date_calc, method=True, type='date', string='Last Invoice Date', help="Date of the last invoice created for this analytic account."),
398         'last_worked_invoiced_date': fields.function(_last_worked_invoiced_date_calc, method=True, type='date', string='Date of Last Invoiced Cost', help="If invoice from the costs, this is the date of the latest work or cost that have been invoiced."),
399         'last_worked_date': fields.function(_last_worked_date_calc, method=True, type='date', string='Date of Last Cost/Work', help="Date of the latest work done on this account."),
400         'hours_qtt_non_invoiced': fields.function(_hours_qtt_non_invoiced_calc, method=True, type='float', string='Uninvoiced Hours', help="Number of hours (from journal of type 'general') that can be invoiced if you invoice based on analytic account."),
401         'hours_qtt_invoiced': fields.function(_hours_qtt_invoiced_calc, method=True, type='float', string='Invoiced Hours', help="Number of hours that can be invoiced plus those that already have been invoiced."),
402         'remaining_hours': fields.function(_remaining_hours_calc, method=True, type='float', string='Remaining Hours', help="Computed using the formula: Maximum Quantity - Hours Tot."),
403         'remaining_ca': fields.function(_remaining_ca_calc, method=True, type='float', string='Remaining Revenue', help="Computed using the formula: Max Invoice Price - Invoiced Amount."),
404         'revenue_per_hour': fields.function(_revenue_per_hour_calc, method=True, type='float', string='Revenue per Hours (real)', help="Computed using the formula: Invoiced Amount / Hours Tot."),
405         'real_margin': fields.function(_real_margin_calc, method=True, type='float', string='Real Margin', help="Computed using the formula: Invoiced Amount - Total Costs."),
406         'theorical_margin': fields.function(_theorical_margin_calc, method=True, type='float', string='Theorical Margin', help="Computed using the formula: Theorial Revenue - Total Costs"),
407         'real_margin_rate': fields.function(_real_margin_rate_calc, method=True, type='float', string='Real Margin Rate (%)', help="Computes using the formula: (Real Margin / Total Costs) * 100."),
408         'month_ids': fields.function(_month, method=True, type='many2many', relation='account_analytic_analysis.summary.month', string='Month'),
409         'user_ids': fields.function(_user, method=True, type="many2many", relation='account_analytic_analysis.summary.user', string='User'),
410     }
411 account_analytic_account()
412
413 class account_analytic_account_summary_user(osv.osv):
414     _name = "account_analytic_analysis.summary.user"
415     _description = "Hours summary by user"
416     _order='user'
417     _auto = False
418     _rec_name = 'user'
419
420     def _unit_amount(self, cr, uid, ids, name, arg, context=None):
421         res = {}
422         account_obj = self.pool.get('account.analytic.account')
423         cr.execute('SELECT MAX(id) FROM res_users')
424         max_user = cr.fetchone()[0]
425         account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids]
426         user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids]
427         account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)])
428         user_set = ','.join([str(x) for x in user_ids])
429         if account_ids2:
430             acc_set = ','.join([str(x) for x in account_ids2])
431             cr.execute('SELECT id, unit_amount ' \
432                     'FROM account_analytic_analysis_summary_user ' \
433                     'WHERE account_id in (%s) ' \
434                         'AND "user" in (%s) ' % (acc_set, user_set))
435             for sum_id, unit_amount in cr.fetchall():
436                 res[sum_id] = unit_amount
437         for obj_id in ids:
438             res.setdefault(obj_id, 0.0)
439             for child_id in account_obj.search(cr, uid,
440                     [('parent_id', 'child_of', [int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)))])]):
441                 if child_id != int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0))):
442                     res[obj_id] += res.get((child_id * max_user) + obj_id -((obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)) * max_user), 0.0)
443         for id in ids:
444             res[id] = round(res.get(id, 0.0), 2)
445         return res
446
447     _columns = {
448         'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
449         'unit_amount': fields.function(_unit_amount, method=True, type='float',
450             string='Total Time'),
451         'user' : fields.many2one('res.users', 'User'),
452     }
453     def init(self, cr):
454         cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (' \
455                 'SELECT ' \
456                     '(u.account_id * u.max_user) + u."user" AS id, ' \
457                     'u.account_id AS account_id, ' \
458                     'u."user" AS "user", ' \
459                     'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
460                 'FROM ' \
461                     '(SELECT ' \
462                         'a.id AS account_id, ' \
463                         'u1.id AS "user", ' \
464                         'MAX(u2.id) AS max_user ' \
465                     'FROM ' \
466                         'res_users AS u1, ' \
467                         'res_users AS u2, ' \
468                         'account_analytic_account AS a ' \
469                     'GROUP BY u1.id, a.id ' \
470                     ') AS u ' \
471                 'LEFT JOIN ' \
472                     '(SELECT ' \
473                         'l.account_id AS account_id, ' \
474                         'l.user_id AS "user", ' \
475                         'SUM(l.unit_amount) AS unit_amount ' \
476                     'FROM account_analytic_line AS l, ' \
477                         'account_analytic_journal AS j ' \
478                     'WHERE j.type = \'general\' ' \
479                     'GROUP BY l.account_id, l.user_id ' \
480                     ') AS l '
481                     'ON (' \
482                         'u.account_id = l.account_id ' \
483                         'AND u."user" = l."user"' \
484                     ') ' \
485                 'GROUP BY u."user", u.account_id, u.max_user' \
486                 ')')
487
488     def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
489         if not context:
490             context={}
491         if not ids:
492             return []
493
494         if fields==None:
495             fields = self._columns.keys()
496
497         # construct a clause for the rules :
498         d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
499
500         # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
501         fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
502
503         res = []
504         cr.execute('SELECT MAX(id) FROM res_users')
505         max_user = cr.fetchone()[0]
506         if len(fields_pre) :
507             fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
508             for i in range(0, len(ids), cr.IN_MAX):
509                 sub_ids = ids[i:i+cr.IN_MAX]
510                 if d1:
511                     cr.execute('select %s from \"%s\" where id in (%s) ' \
512                             'and account_id in (%s) ' \
513                             'and "user" in (%s) and %s order by %s' % \
514                             (','.join(fields_pre2 + ['id']), self._table,
515                                 ','.join([str(x) for x in sub_ids]),
516                                 ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
517                                 ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), d1,
518                                 self._order),d2)
519                     if not cr.rowcount == len({}.fromkeys(sub_ids)):
520                         raise except_orm(_('AccessError'),
521                                 _('You try to bypass an access rule (Document type: %s).') % self._description)
522                 else:
523                     cr.execute('select %s from \"%s\" where id in (%s) ' \
524                             'and account_id in (%s) ' \
525                             'and "user" in (%s) order by %s' % \
526                             (','.join(fields_pre2 + ['id']), self._table,
527                                 ','.join([str(x) for x in sub_ids]),
528                                 ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
529                                 ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]),
530                                 self._order))
531                 res.extend(cr.dictfetchall())
532         else:
533             res = map(lambda x: {'id': x}, ids)
534
535         for f in fields_pre:
536             if self._columns[f].translate:
537                 ids = map(lambda x: x['id'], res)
538                 res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
539                 for r in res:
540                     r[f] = res_trans.get(r['id'], False) or r[f]
541
542         for table in self._inherits:
543             col = self._inherits[table]
544             cols = intersect(self._inherit_fields.keys(), fields)
545             if not cols:
546                 continue
547             res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
548
549             res3 = {}
550             for r in res2:
551                 res3[r['id']] = r
552                 del r['id']
553
554             for record in res:
555                 record.update(res3[record[col]])
556                 if col not in fields:
557                     del record[col]
558
559         # all fields which need to be post-processed by a simple function (symbol_get)
560         fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
561         if fields_post:
562             # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
563             # to get the _symbol_get in each occurence
564             for r in res:
565                 for f in fields_post:
566                     r[f] = self.columns[f]._symbol_get(r[f])
567         ids = map(lambda x: x['id'], res)
568
569         # all non inherited fields for which the attribute whose name is in load is False
570         fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
571         for f in fields_post:
572             # get the value of that field for all records/ids
573             res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
574             for record in res:
575                 record[f] = res2[record['id']]
576
577         return res
578
579 account_analytic_account_summary_user()
580
581 class account_analytic_account_summary_month(osv.osv):
582     _name = "account_analytic_analysis.summary.month"
583     _description = "Hours summary by month"
584     _auto = False
585     _rec_name = 'month'
586     _order = 'month'
587
588     def _unit_amount(self, cr, uid, ids, name, arg, context=None):
589         res = {}
590         account_obj = self.pool.get('account.analytic.account')
591         account_ids = [int(str(int(x))[:-6]) for x in ids]
592         month_ids = [int(str(int(x))[-6:]) for x in ids]
593         account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)])
594         month_set = ','.join([str(x) for x in month_ids])
595         if account_ids2:
596             acc_set = ','.join([str(x) for x in account_ids2])
597             cr.execute('SELECT id, unit_amount ' \
598                     'FROM account_analytic_analysis_summary_month ' \
599                     'WHERE account_id in (%s) ' \
600                         'AND month_id in (%s) ' % \
601                         (acc_set, month_set))
602             for sum_id, unit_amount in cr.fetchall():
603                 res[sum_id] = unit_amount
604         for obj_id in ids:
605             res.setdefault(obj_id, 0.0)
606             for child_id in account_obj.search(cr, uid,
607                     [('parent_id', 'child_of', [int(str(int(obj_id))[:-6])])]):
608                 if child_id != int(str(int(obj_id))[:-6]):
609                     res[obj_id] += res.get(int(child_id * 1000000 + int(obj_id)), 0.0)
610         for id in ids:
611             res[id] = round(res.get(id, 0.0), 2)
612         return res
613
614     _columns = {
615         'account_id': fields.many2one('account.analytic.account', 'Analytic Account',
616             readonly=True),
617         'unit_amount': fields.function(_unit_amount, method=True, type='float',
618             string='Total Time'),
619         'month': fields.char('Month', size=25, readonly=True),
620     }
621
622     def init(self, cr):
623         tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_month')
624         cr.execute('CREATE VIEW account_analytic_analysis_summary_month AS (' \
625                 'SELECT ' \
626                     '(TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') + (d.account_id  * 1000000))::integer AS id, ' \
627                     'd.account_id AS account_id, ' \
628                     'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \
629                     'TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') AS month_id, ' \
630                     'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
631                 'FROM ' \
632                     '(SELECT ' \
633                         'd2.account_id, ' \
634                         'd2.month ' \
635                     'FROM ' \
636                         '(SELECT ' \
637                             'a.id AS account_id, ' \
638                             'l.month AS month ' \
639                         'FROM ' \
640                             '(SELECT ' \
641                                 'DATE_TRUNC(\'month\', l.date) AS month ' \
642                             'FROM account_analytic_line AS l, ' \
643                                 'account_analytic_journal AS j ' \
644                             'WHERE j.type = \'general\' ' \
645                             'GROUP BY DATE_TRUNC(\'month\', l.date) ' \
646                             ') AS l, ' \
647                             'account_analytic_account AS a ' \
648                         'GROUP BY l.month, a.id ' \
649                         ') AS d2 ' \
650                     'GROUP BY d2.account_id, d2.month ' \
651                     ') AS d ' \
652                 'LEFT JOIN ' \
653                     '(SELECT ' \
654                         'l.account_id AS account_id, ' \
655                         'DATE_TRUNC(\'month\', l.date) AS month, ' \
656                         'SUM(l.unit_amount) AS unit_amount ' \
657                     'FROM account_analytic_line AS l, ' \
658                         'account_analytic_journal AS j ' \
659                     'WHERE j.type = \'general\' ' \
660                     'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \
661                     ') AS l '
662                     'ON (' \
663                         'd.account_id = l.account_id ' \
664                         'AND d.month = l.month' \
665                     ') ' \
666                 'GROUP BY d.month, d.account_id ' \
667                 ')')
668
669     def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
670         if not context:
671             context={}
672         if not ids:
673             return []
674
675         if fields==None:
676             fields = self._columns.keys()
677
678         # construct a clause for the rules :
679         d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
680
681         # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
682         fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
683
684         res = []
685         if len(fields_pre) :
686             fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
687             for i in range(0, len(ids), cr.IN_MAX):
688                 sub_ids = ids[i:i+cr.IN_MAX]
689                 if d1:
690                     cr.execute('select %s from \"%s\" where id in (%s) ' \
691                             'and account_id in (%s) ' \
692                             'and month_id in (%s) and %s order by %s' % \
693                             (','.join(fields_pre2 + ['id']), self._table,
694                                 ','.join([str(x) for x in sub_ids]),
695                                 ','.join([str(x)[:-6] for x in sub_ids]),
696                                 ','.join([str(x)[-6:] for x in sub_ids]), d1,
697                                 self._order),d2)
698                     if not cr.rowcount == len({}.fromkeys(sub_ids)):
699                         raise except_orm(_('AccessError'),
700                                 _('You try to bypass an access rule (Document type: %s).') % self._description)
701                 else:
702                     cr.execute('select %s from \"%s\" where id in (%s) ' \
703                             'and account_id in (%s) ' \
704                             'and month_id in (%s) order by %s' % \
705                             (','.join(fields_pre2 + ['id']), self._table,
706                                 ','.join([str(x) for x in sub_ids]),
707                                 ','.join([str(x)[:-6] for x in sub_ids]),
708                                 ','.join([str(x)[-6:] for x in sub_ids]),
709                                 self._order))
710                 res.extend(cr.dictfetchall())
711         else:
712             res = map(lambda x: {'id': x}, ids)
713
714         for f in fields_pre:
715             if self._columns[f].translate:
716                 ids = map(lambda x: x['id'], res)
717                 res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
718                 for r in res:
719                     r[f] = res_trans.get(r['id'], False) or r[f]
720
721         for table in self._inherits:
722             col = self._inherits[table]
723             cols = intersect(self._inherit_fields.keys(), fields)
724             if not cols:
725                 continue
726             res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
727
728             res3 = {}
729             for r in res2:
730                 res3[r['id']] = r
731                 del r['id']
732
733             for record in res:
734                 record.update(res3[record[col]])
735                 if col not in fields:
736                     del record[col]
737
738         # all fields which need to be post-processed by a simple function (symbol_get)
739         fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
740         if fields_post:
741             # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
742             # to get the _symbol_get in each occurence
743             for r in res:
744                 for f in fields_post:
745                     r[f] = self.columns[f]._symbol_get(r[f])
746         ids = map(lambda x: x['id'], res)
747
748         # all non inherited fields for which the attribute whose name is in load is False
749         fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
750         for f in fields_post:
751             # get the value of that field for all records/ids
752             res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
753             for record in res:
754                 record[f] = res2[record['id']]
755
756         return res
757
758 account_analytic_account_summary_month()
759
760
761 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
762