1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 from osv import osv, fields
23 from osv.orm import intersect
25 from tools.translate import _
27 class account_analytic_account(osv.osv):
28 _name = "account.analytic.account"
29 _inherit = "account.analytic.account"
31 def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context=None):
33 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
35 cr.execute("select account_analytic_line.account_id, COALESCE(sum(amount_currency),0.0) \
36 from account_analytic_line \
37 join account_analytic_journal \
38 on account_analytic_line.journal_id = account_analytic_journal.id \
39 where account_analytic_line.account_id IN %s \
40 and account_analytic_journal.type = 'sale' \
41 group by account_analytic_line.account_id" ,(parent_ids,))
42 for account_id, sum in cr.fetchall():
43 res[account_id] = round(sum,2)
45 return self._compute_currency_for_level_tree(cr, uid, ids, parent_ids, res, context)
47 def _ca_to_invoice_calc(self, cr, uid, ids, name, arg, context=None):
50 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
52 # Amount uninvoiced hours to invoice at sale price
54 # This computation doesn't take care of pricelist !
55 # Just consider list_price
56 cr.execute("""SELECT account_analytic_account.id, \
57 COALESCE(sum (product_template.list_price * \
58 account_analytic_line.unit_amount * \
59 ((100-hr_timesheet_invoice_factor.factor)/100)),0.0) \
61 FROM product_template \
62 join product_product \
63 on product_template.id = product_product.product_tmpl_id \
64 JOIN account_analytic_line \
65 on account_analytic_line.product_id = product_product.id \
66 JOIN account_analytic_journal \
67 on account_analytic_line.journal_id = account_analytic_journal.id \
68 JOIN account_analytic_account \
69 on account_analytic_account.id = account_analytic_line.account_id \
70 JOIN hr_timesheet_invoice_factor \
71 on hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \
72 WHERE account_analytic_account.id IN %s \
73 AND account_analytic_line.invoice_id is null \
74 AND account_analytic_line.to_invoice IS NOT NULL \
75 and account_analytic_journal.type in ('purchase','general') \
76 GROUP BY account_analytic_account.id;""",(parent_ids,))
77 for account_id, sum in cr.fetchall():
78 res[account_id] = round(sum,2)
81 res.setdefault(obj_id, 0.0)
82 res2.setdefault(obj_id, 0.0)
83 for child_id in self.search(cr, uid,
84 [('parent_id', 'child_of', [obj_id])]):
85 if child_id != obj_id:
86 res[obj_id] += res.get(child_id, 0.0)
87 res2[obj_id] += res2.get(child_id, 0.0)
88 # sum both result on account_id
90 res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2)
93 def _hours_qtt_non_invoiced_calc (self, cr, uid, ids, name, arg, context=None):
95 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
97 cr.execute("select account_analytic_line.account_id, COALESCE(sum(unit_amount),0.0) \
98 from account_analytic_line \
99 join account_analytic_journal \
100 on account_analytic_line.journal_id = account_analytic_journal.id \
101 where account_analytic_line.account_id IN %s \
102 and account_analytic_journal.type='general' \
103 and invoice_id is null \
104 AND to_invoice IS NOT NULL \
105 GROUP BY account_analytic_line.account_id;",(parent_ids,))
106 for account_id, sum in cr.fetchall():
107 res[account_id] = round(sum,2)
109 res.setdefault(obj_id, 0.0)
110 for child_id in self.search(cr, uid,
111 [('parent_id', 'child_of', [obj_id])]):
112 if child_id != obj_id:
113 res[obj_id] += res.get(child_id, 0.0)
115 res[id] = round(res.get(id, 0.0),2)
118 def _hours_quantity_calc(self, cr, uid, ids, name, arg, context=None):
120 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
122 cr.execute("select account_analytic_line.account_id,COALESCE(SUM(unit_amount),0.0) \
123 from account_analytic_line \
124 join account_analytic_journal \
125 on account_analytic_line.journal_id = account_analytic_journal.id \
126 where account_analytic_line.account_id IN %s \
127 and account_analytic_journal.type='general' \
128 GROUP BY account_analytic_line.account_id",(parent_ids,))
130 for account_id, sum in ff:
131 res[account_id] = round(sum,2)
133 res.setdefault(obj_id, 0.0)
134 for child_id in self.search(cr, uid,
135 [('parent_id', 'child_of', [obj_id])]):
136 if child_id != obj_id:
137 res[obj_id] += res.get(child_id, 0.0)
139 res[id] = round(res.get(id, 0.0),2)
142 def _total_cost_calc(self, cr, uid, ids, name, arg, context=None):
144 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
146 cr.execute("""select account_analytic_line.account_id,COALESCE(sum(amount_currency),0.0) \
148 from account_analytic_line \
149 join account_analytic_journal \
150 on account_analytic_line.journal_id = account_analytic_journal.id \
151 where account_analytic_line.account_id IN %s \
153 GROUP BY account_analytic_line.account_id""",(parent_ids,))
154 for account_id, sum in cr.fetchall():
155 res[account_id] = round(sum,2)
156 return self._compute_currency_for_level_tree(cr, uid, ids, parent_ids, res, context)
158 # TODO Take care of pricelist and purchase !
159 def _ca_theorical_calc(self, cr, uid, ids, name, arg, context=None):
162 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
164 # This computation doesn't take care of pricelist !
165 # Just consider list_price
167 cr.execute("""select account_analytic_line.account_id as account_id, \
168 COALESCE(sum((account_analytic_line.unit_amount * pt.list_price) \
169 - (account_analytic_line.unit_amount * pt.list_price \
170 * hr.factor)),0.0) as somme
171 from account_analytic_line \
172 left join account_analytic_journal \
173 on (account_analytic_line.journal_id = account_analytic_journal.id) \
174 join product_product pp \
175 on (account_analytic_line.product_id = pp.id) \
176 join product_template pt \
177 on (pp.product_tmpl_id = pt.id) \
178 join account_analytic_account a \
179 on (a.id=account_analytic_line.account_id) \
180 join hr_timesheet_invoice_factor hr \
181 on (hr.id=a.to_invoice) \
182 where account_analytic_line.account_id IN %s \
183 and a.to_invoice IS NOT NULL \
184 and account_analytic_journal.type IN ('purchase','general')
185 GROUP BY account_analytic_line.account_id""",(parent_ids,))
186 for account_id, sum in cr.fetchall():
187 res2[account_id] = round(sum,2)
190 res.setdefault(obj_id, 0.0)
191 res2.setdefault(obj_id, 0.0)
192 for child_id in self.search(cr, uid,
193 [('parent_id', 'child_of', [obj_id])]):
194 if child_id != obj_id:
195 res[obj_id] += res.get(child_id, 0.0)
196 res[obj_id] += res2.get(child_id, 0.0)
198 # sum both result on account_id
200 res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2)
203 def _last_worked_date_calc (self, cr, uid, ids, name, arg, context = None):
205 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
207 cr.execute("select account_analytic_line.account_id, max(date) \
208 from account_analytic_line \
209 where account_id IN %s \
210 and invoice_id is null \
211 GROUP BY account_analytic_line.account_id" ,(parent_ids,))
212 for account_id, sum in cr.fetchall():
213 res[account_id] = sum
215 res.setdefault(obj_id, '')
216 for child_id in self.search(cr, uid,
217 [('parent_id', 'child_of', [obj_id])]):
218 if res[obj_id] < res.get(child_id, ''):
219 res[obj_id] = res.get(child_id, '')
221 res[id] = res.get(id, '')
224 def _last_invoice_date_calc (self, cr, uid, ids, name, arg, context=None):
226 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
228 cr.execute ("select account_analytic_line.account_id, \
229 date(max(account_invoice.date_invoice)) \
230 from account_analytic_line \
231 join account_invoice \
232 on account_analytic_line.invoice_id = account_invoice.id \
233 where account_analytic_line.account_id IN %s \
234 and account_analytic_line.invoice_id is not null \
235 GROUP BY account_analytic_line.account_id",(parent_ids,))
236 for account_id, sum in cr.fetchall():
237 res[account_id] = sum
239 res.setdefault(obj_id, '')
240 for child_id in self.search(cr, uid,
241 [('parent_id', 'child_of', [obj_id])]):
242 if res[obj_id] < res.get(child_id, ''):
243 res[obj_id] = res.get(child_id, '')
245 res[id] = res.get(id, '')
248 def _last_worked_invoiced_date_calc (self, cr, uid, ids, name, arg, context=None):
250 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
252 cr.execute("select account_analytic_line.account_id, max(date) \
253 from account_analytic_line \
254 where account_id IN %s \
255 and invoice_id is not null \
256 GROUP BY account_analytic_line.account_id;",(parent_ids,))
257 for account_id, sum in cr.fetchall():
258 res[account_id] = sum
260 res.setdefault(obj_id, '')
261 for child_id in self.search(cr, uid,
262 [('parent_id', 'child_of', [obj_id])]):
263 if res[obj_id] < res.get(child_id, ''):
264 res[obj_id] = res.get(child_id, '')
266 res[id] = res.get(id, '')
269 def _remaining_hours_calc(self, cr, uid, ids, name, arg, context=None):
271 for account in self.browse(cr, uid, ids):
272 if account.quantity_max != 0:
273 res[account.id] = account.quantity_max - account.hours_quantity
277 res[id] = round(res.get(id, 0.0),2)
280 def _hours_qtt_invoiced_calc(self, cr, uid, ids, name, arg, context=None):
282 for account in self.browse(cr, uid, ids):
283 res[account.id] = account.hours_quantity - account.hours_qtt_non_invoiced
284 if res[account.id] < 0:
287 res[id] = round(res.get(id, 0.0),2)
290 def _revenue_per_hour_calc(self, cr, uid, ids, name, arg, context=None):
292 for account in self.browse(cr, uid, ids):
293 if account.hours_qtt_invoiced == 0:
296 res[account.id] = account.ca_invoiced / account.hours_qtt_invoiced
298 res[id] = round(res.get(id, 0.0),2)
301 def _real_margin_rate_calc(self, cr, uid, ids, name, arg, context=None):
303 for account in self.browse(cr, uid, ids):
304 if account.ca_invoiced == 0:
306 elif account.total_cost != 0.0:
307 res[account.id] = -(account.real_margin / account.total_cost) * 100
309 res[account.id] = 0.0
311 res[id] = round(res.get(id, 0.0),2)
314 def _remaining_ca_calc(self, cr, uid, ids, name, arg, context=None):
316 for account in self.browse(cr, uid, ids):
317 if account.amount_max != 0:
318 res[account.id] = account.amount_max - account.ca_invoiced
322 res[id] = round(res.get(id, 0.0),2)
325 def _real_margin_calc(self, cr, uid, ids, name, arg, context=None):
327 for account in self.browse(cr, uid, ids):
328 res[account.id] = account.ca_invoiced + account.total_cost
330 res[id] = round(res.get(id, 0.0),2)
333 def _theorical_margin_calc(self, cr, uid, ids, name, arg, context=None):
335 for account in self.browse(cr, uid, ids):
336 res[account.id] = account.ca_theorical + account.total_cost
338 res[id] = round(res.get(id, 0.0),2)
341 def _month(self, cr, uid, ids, name, arg, context=None):
344 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
346 cr.execute('SELECT DISTINCT(month_id) FROM account_analytic_analysis_summary_month ' \
347 'WHERE account_id IN %s AND unit_amount <> 0.0',(parent_ids,))
348 res[id] = [int(id * 1000000 + int(x[0])) for x in cr.fetchall()]
353 def _user(self, cr, uid, ids, name, arg, context=None):
355 cr.execute('SELECT MAX(id) FROM res_users')
356 max_user = cr.fetchone()[0]
358 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
360 cr.execute('SELECT DISTINCT("user") FROM account_analytic_analysis_summary_user ' \
361 'WHERE account_id IN %s AND unit_amount <> 0.0',(parent_ids,))
362 res[id] = [int((id * max_user) + x[0]) for x in cr.fetchall()]
368 'ca_invoiced': fields.function(_ca_invoiced_calc, method=True, type='float', string='Invoiced Amount', help="Total customer invoiced amount for this account."),
369 '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."),
370 '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."),
371 '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."),
372 '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'."),
373 '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."),
374 '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."),
375 '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."),
376 '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."),
377 '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."),
378 'remaining_hours': fields.function(_remaining_hours_calc, method=True, type='float', string='Remaining Hours', help="Computed using the formula: Maximum Quantity - Hours Tot."),
379 'remaining_ca': fields.function(_remaining_ca_calc, method=True, type='float', string='Remaining Revenue', help="Computed using the formula: Max Invoice Price - Invoiced Amount."),
380 '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."),
381 'real_margin': fields.function(_real_margin_calc, method=True, type='float', string='Real Margin', help="Computed using the formula: Invoiced Amount - Total Costs."),
382 'theorical_margin': fields.function(_theorical_margin_calc, method=True, type='float', string='Theorical Margin', help="Computed using the formula: Theorial Revenue - Total Costs"),
383 '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."),
384 'month_ids': fields.function(_month, method=True, type='many2many', relation='account_analytic_analysis.summary.month', string='Month'),
385 'user_ids': fields.function(_user, method=True, type="many2many", relation='account_analytic_analysis.summary.user', string='User'),
387 account_analytic_account()
389 class account_analytic_account_summary_user(osv.osv):
390 _name = "account_analytic_analysis.summary.user"
391 _description = "Hours Summary by User"
396 def _unit_amount(self, cr, uid, ids, name, arg, context=None):
398 account_obj = self.pool.get('account.analytic.account')
399 cr.execute('SELECT MAX(id) FROM res_users')
400 max_user = cr.fetchone()[0]
401 account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids]
402 user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids]
403 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', account_ids)]))
405 cr.execute('SELECT id, unit_amount ' \
406 'FROM account_analytic_analysis_summary_user ' \
407 'WHERE account_id IN %s ' \
408 'AND "user" IN %s',(parent_ids, user_ids,))
409 for sum_id, unit_amount in cr.fetchall():
410 res[sum_id] = unit_amount
412 res.setdefault(obj_id, 0.0)
413 for child_id in account_obj.search(cr, uid,
414 [('parent_id', 'child_of', [int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)))])]):
415 if child_id != int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0))):
416 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)
418 res[id] = round(res.get(id, 0.0), 2)
422 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
423 'unit_amount': fields.function(_unit_amount, method=True, type='float',
424 string='Total Time'),
425 'user' : fields.many2one('res.users', 'User'),
429 tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_user')
430 cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (' \
432 '(u.account_id * u.max_user) + u."user" AS id, ' \
433 'u.account_id AS account_id, ' \
434 'u."user" AS "user", ' \
435 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
438 'a.id AS account_id, ' \
439 'u1.id AS "user", ' \
440 'MAX(u2.id) AS max_user ' \
442 'res_users AS u1, ' \
443 'res_users AS u2, ' \
444 'account_analytic_account AS a ' \
445 'GROUP BY u1.id, a.id ' \
449 'l.account_id AS account_id, ' \
450 'l.user_id AS "user", ' \
451 'SUM(l.unit_amount) AS unit_amount ' \
452 'FROM account_analytic_line AS l, ' \
453 'account_analytic_journal AS j ' \
454 'WHERE (j.type = \'general\') and (j.id=l.journal_id) ' \
455 'GROUP BY l.account_id, l.user_id ' \
458 'u.account_id = l.account_id ' \
459 'AND u."user" = l."user"' \
461 'GROUP BY u."user", u.account_id, u.max_user' \
464 def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
471 fields = self._columns.keys()
473 # construct a clause for the rules :
474 d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
476 # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
477 fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
480 cr.execute('SELECT MAX(id) FROM res_users')
481 max_user = cr.fetchone()[0]
483 fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
484 for i in range(0, len(ids), cr.IN_MAX):
485 sub_ids = ids[i:i+cr.IN_MAX]
487 cr.execute('select %s from \"%s\" where id IN (%s) ' \
488 'and account_id IN (%s) ' \
489 'and "user" IN (%s) and %s order by %s' % \
490 (','.join(fields_pre2 + ['id']), self._table,
491 ','.join([str(x) for x in sub_ids]),
492 ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
493 ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), d1,
495 if not cr.rowcount == len({}.fromkeys(sub_ids)):
496 raise except_orm(_('AccessError'),
497 _('You try to bypass an access rule (Document type: %s).') % self._description)
499 cr.execute('select %s from \"%s\" where id IN (%s) ' \
500 'and account_id IN (%s) ' \
501 'and "user" IN (%s) order by %s' % \
502 (','.join(fields_pre2 + ['id']), self._table,
503 ','.join([str(x) for x in sub_ids]),
504 ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
505 ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]),
507 res.extend(cr.dictfetchall())
509 res = map(lambda x: {'id': x}, ids)
512 if self._columns[f].translate:
513 ids = map(lambda x: x['id'], res)
514 res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
516 r[f] = res_trans.get(r['id'], False) or r[f]
518 for table in self._inherits:
519 col = self._inherits[table]
520 cols = intersect(self._inherit_fields.keys(), fields)
523 res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
531 record.update(res3[record[col]])
532 if col not in fields:
535 # all fields which need to be post-processed by a simple function (symbol_get)
536 fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
538 # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
539 # to get the _symbol_get in each occurence
541 for f in fields_post:
542 r[f] = self.columns[f]._symbol_get(r[f])
543 ids = map(lambda x: x['id'], res)
545 # all non inherited fields for which the attribute whose name is in load is False
546 fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
547 for f in fields_post:
548 # get the value of that field for all records/ids
549 res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
551 record[f] = res2[record['id']]
555 account_analytic_account_summary_user()
557 class account_analytic_account_summary_month(osv.osv):
558 _name = "account_analytic_analysis.summary.month"
559 _description = "Hours summary by month"
564 def _unit_amount(self, cr, uid, ids, name, arg, context=None):
566 account_obj = self.pool.get('account.analytic.account')
567 account_ids = [int(str(int(x))[:-6]) for x in ids]
568 month_ids = [int(str(int(x))[-6:]) for x in ids]
569 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', account_ids)]))
571 cr.execute('SELECT id, unit_amount ' \
572 'FROM account_analytic_analysis_summary_month ' \
573 'WHERE account_id IN %s ' \
574 'AND month_id IN %s ',(parent_ids, month_ids,))
575 for sum_id, unit_amount in cr.fetchall():
576 res[sum_id] = unit_amount
578 res.setdefault(obj_id, 0.0)
579 for child_id in account_obj.search(cr, uid,
580 [('parent_id', 'child_of', [int(str(int(obj_id))[:-6])])]):
581 if child_id != int(str(int(obj_id))[:-6]):
582 res[obj_id] += res.get(int(child_id * 1000000 + int(str(int(obj_id))[-6:])), 0.0)
584 res[id] = round(res.get(id, 0.0), 2)
588 'account_id': fields.many2one('account.analytic.account', 'Analytic Account',
590 'unit_amount': fields.function(_unit_amount, method=True, type='float',
591 string='Total Time'),
592 'month': fields.char('Month', size=25, readonly=True),
596 tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_month')
597 cr.execute('CREATE VIEW account_analytic_analysis_summary_month AS (' \
599 '(TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') + (d.account_id * 1000000))::integer AS id, ' \
600 'd.account_id AS account_id, ' \
601 'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \
602 'TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') AS month_id, ' \
603 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
610 'a.id AS account_id, ' \
611 'l.month AS month ' \
614 'DATE_TRUNC(\'month\', l.date) AS month ' \
615 'FROM account_analytic_line AS l, ' \
616 'account_analytic_journal AS j ' \
617 'WHERE j.type = \'general\' ' \
618 'GROUP BY DATE_TRUNC(\'month\', l.date) ' \
620 'account_analytic_account AS a ' \
621 'GROUP BY l.month, a.id ' \
623 'GROUP BY d2.account_id, d2.month ' \
627 'l.account_id AS account_id, ' \
628 'DATE_TRUNC(\'month\', l.date) AS month, ' \
629 'SUM(l.unit_amount) AS unit_amount ' \
630 'FROM account_analytic_line AS l, ' \
631 'account_analytic_journal AS j ' \
632 'WHERE (j.type = \'general\') and (j.id=l.journal_id) ' \
633 'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \
636 'd.account_id = l.account_id ' \
637 'AND d.month = l.month' \
639 'GROUP BY d.month, d.account_id ' \
642 def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
649 fields = self._columns.keys()
651 # construct a clause for the rules :
652 d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
654 # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
655 fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
659 fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
660 for i in range(0, len(ids), cr.IN_MAX):
661 sub_ids = ids[i:i+cr.IN_MAX]
663 cr.execute('select %s from \"%s\" where id IN (%s) ' \
664 'and account_id IN (%s) ' \
665 'and month_id IN (%s) and %s order by %s' % \
666 (','.join(fields_pre2 + ['id']), self._table,
667 ','.join([str(x) for x in sub_ids]),
668 ','.join([str(x)[:-6] for x in sub_ids]),
669 ','.join([str(x)[-6:] for x in sub_ids]), d1,
671 if not cr.rowcount == len({}.fromkeys(sub_ids)):
672 raise except_orm(_('AccessError'),
673 _('You try to bypass an access rule (Document type: %s).') % self._description)
675 cr.execute('select %s from \"%s\" where id IN (%s) ' \
676 'and account_id IN (%s) ' \
677 'and month_id IN (%s) order by %s' % \
678 (','.join(fields_pre2 + ['id']), self._table,
679 ','.join([str(x) for x in sub_ids]),
680 ','.join([str(x)[:-6] for x in sub_ids]),
681 ','.join([str(x)[-6:] for x in sub_ids]),
683 res.extend(cr.dictfetchall())
685 res = map(lambda x: {'id': x}, ids)
688 if self._columns[f].translate:
689 ids = map(lambda x: x['id'], res)
690 res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
692 r[f] = res_trans.get(r['id'], False) or r[f]
694 for table in self._inherits:
695 col = self._inherits[table]
696 cols = intersect(self._inherit_fields.keys(), fields)
699 res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
707 record.update(res3[record[col]])
708 if col not in fields:
711 # all fields which need to be post-processed by a simple function (symbol_get)
712 fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
714 # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
715 # to get the _symbol_get in each occurence
717 for f in fields_post:
718 r[f] = self.columns[f]._symbol_get(r[f])
719 ids = map(lambda x: x['id'], res)
721 # all non inherited fields for which the attribute whose name is in load is False
722 fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
723 for f in fields_post:
724 # get the value of that field for all records/ids
725 res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
727 record[f] = res2[record['id']]
731 account_analytic_account_summary_month()
733 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: