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 ##############################################################################
23 from osv import osv, fields
24 from osv.orm import intersect
26 from tools.translate import _
29 class account_analytic_account(osv.osv):
30 _name = "account.analytic.account"
31 _inherit = "account.analytic.account"
33 def _ca_invoiced_calc(self, cr, uid, ids, name, arg, context={}):
35 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
37 cr.execute("select account_analytic_line.account_id, COALESCE(sum(amount_currency),0.0) \
38 from account_analytic_line \
39 join account_analytic_journal \
40 on account_analytic_line.journal_id = account_analytic_journal.id \
41 where account_analytic_line.account_id IN %s \
42 and account_analytic_journal.type = 'sale' \
43 group by account_analytic_line.account_id" ,(parent_ids,))
44 for account_id, sum in cr.fetchall():
45 res[account_id] = round(sum,2)
47 return self._compute_currency_for_level_tree(cr, uid, ids, parent_ids, res, context)
49 def _ca_to_invoice_calc(self, cr, uid, ids, name, arg, context={}):
52 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
54 # Amount uninvoiced hours to invoice at sale price
56 # This computation doesn't take care of pricelist !
57 # Just consider list_price
58 cr.execute("""SELECT account_analytic_account.id, \
59 COALESCE(sum (product_template.list_price * \
60 account_analytic_line.unit_amount * \
61 ((100-hr_timesheet_invoice_factor.factor)/100)),0.0) \
63 FROM product_template \
64 join product_product \
65 on product_template.id = product_product.product_tmpl_id \
66 JOIN account_analytic_line \
67 on account_analytic_line.product_id = product_product.id \
68 JOIN account_analytic_journal \
69 on account_analytic_line.journal_id = account_analytic_journal.id \
70 JOIN account_analytic_account \
71 on account_analytic_account.id = account_analytic_line.account_id \
72 JOIN hr_timesheet_invoice_factor \
73 on hr_timesheet_invoice_factor.id = account_analytic_account.to_invoice \
74 WHERE account_analytic_account.id IN %s \
75 AND account_analytic_line.invoice_id is null \
76 AND account_analytic_line.to_invoice IS NOT NULL \
77 and account_analytic_journal.type in ('purchase','general') \
78 GROUP BY account_analytic_account.id;""",(parent_ids,))
79 for account_id, sum in cr.fetchall():
80 res[account_id] = round(sum,2)
83 res.setdefault(obj_id, 0.0)
84 res2.setdefault(obj_id, 0.0)
85 for child_id in self.search(cr, uid,
86 [('parent_id', 'child_of', [obj_id])]):
87 if child_id != obj_id:
88 res[obj_id] += res.get(child_id, 0.0)
89 res2[obj_id] += res2.get(child_id, 0.0)
90 # sum both result on account_id
92 res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2)
95 def _hours_qtt_non_invoiced_calc (self, cr, uid, ids, name, arg, context={}):
97 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
99 cr.execute("select account_analytic_line.account_id, COALESCE(sum(unit_amount),0.0) \
100 from account_analytic_line \
101 join account_analytic_journal \
102 on account_analytic_line.journal_id = account_analytic_journal.id \
103 where account_analytic_line.account_id IN %s \
104 and account_analytic_journal.type='general' \
105 and invoice_id is null \
106 AND to_invoice IS NOT NULL \
107 GROUP BY account_analytic_line.account_id;",(parent_ids,))
108 for account_id, sum in cr.fetchall():
109 res[account_id] = round(sum,2)
111 res.setdefault(obj_id, 0.0)
112 for child_id in self.search(cr, uid,
113 [('parent_id', 'child_of', [obj_id])]):
114 if child_id != obj_id:
115 res[obj_id] += res.get(child_id, 0.0)
117 res[id] = round(res.get(id, 0.0),2)
120 def _hours_quantity_calc(self, cr, uid, ids, name, arg, context={}):
122 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
124 cr.execute("select account_analytic_line.account_id,COALESCE(SUM(unit_amount),0.0) \
125 from account_analytic_line \
126 join account_analytic_journal \
127 on account_analytic_line.journal_id = account_analytic_journal.id \
128 where account_analytic_line.account_id IN %s \
129 and account_analytic_journal.type='general' \
130 GROUP BY account_analytic_line.account_id",(parent_ids,))
132 for account_id, sum in ff:
133 res[account_id] = round(sum,2)
135 res.setdefault(obj_id, 0.0)
136 for child_id in self.search(cr, uid,
137 [('parent_id', 'child_of', [obj_id])]):
138 if child_id != obj_id:
139 res[obj_id] += res.get(child_id, 0.0)
141 res[id] = round(res.get(id, 0.0),2)
144 def _total_cost_calc(self, cr, uid, ids, name, arg, context={}):
146 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
148 cr.execute("""select account_analytic_line.account_id,COALESCE(sum(amount_currency),0.0) \
150 from account_analytic_line \
151 join account_analytic_journal \
152 on account_analytic_line.journal_id = account_analytic_journal.id \
153 where account_analytic_line.account_id IN %s \
155 GROUP BY account_analytic_line.account_id""",(parent_ids,))
156 for account_id, sum in cr.fetchall():
157 res[account_id] = round(sum,2)
158 return self._compute_currency_for_level_tree(cr, uid, ids, parent_ids, res, context)
160 # TODO Take care of pricelist and purchase !
161 def _ca_theorical_calc(self, cr, uid, ids, name, arg, context={}):
164 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
166 # This computation doesn't take care of pricelist !
167 # Just consider list_price
169 cr.execute("""select account_analytic_line.account_id as account_id, \
170 COALESCE(sum((account_analytic_line.unit_amount * pt.list_price) \
171 - (account_analytic_line.unit_amount * pt.list_price \
172 * hr.factor)),0.0) as somme
173 from account_analytic_line \
174 left join account_analytic_journal \
175 on (account_analytic_line.journal_id = account_analytic_journal.id) \
176 join product_product pp \
177 on (account_analytic_line.product_id = pp.id) \
178 join product_template pt \
179 on (pp.product_tmpl_id = pt.id) \
180 join account_analytic_account a \
181 on (a.id=account_analytic_line.account_id) \
182 join hr_timesheet_invoice_factor hr \
183 on (hr.id=a.to_invoice) \
184 where account_analytic_line.account_id IN %s \
185 and a.to_invoice IS NOT NULL \
186 and account_analytic_journal.type in ('purchase','general')
187 GROUP BY account_analytic_line.account_id""",(parent_ids,))
188 for account_id, sum in cr.fetchall():
189 res2[account_id] = round(sum,2)
192 res.setdefault(obj_id, 0.0)
193 res2.setdefault(obj_id, 0.0)
194 for child_id in self.search(cr, uid,
195 [('parent_id', 'child_of', [obj_id])]):
196 if child_id != obj_id:
197 res[obj_id] += res.get(child_id, 0.0)
198 res[obj_id] += res2.get(child_id, 0.0)
200 # sum both result on account_id
202 res[id] = round(res.get(id, 0.0),2) + round(res2.get(id, 0.0),2)
205 def _last_worked_date_calc (self, cr, uid, ids, name, arg, context={}):
207 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
209 cr.execute("select account_analytic_line.account_id, max(date) \
210 from account_analytic_line \
211 where account_id IN %s \
212 and invoice_id is null \
213 GROUP BY account_analytic_line.account_id" ,(parent_ids,))
214 for account_id, sum in cr.fetchall():
215 res[account_id] = sum
217 res.setdefault(obj_id, '')
218 for child_id in self.search(cr, uid,
219 [('parent_id', 'child_of', [obj_id])]):
220 if res[obj_id] < res.get(child_id, ''):
221 res[obj_id] = res.get(child_id, '')
223 res[id] = res.get(id, '')
226 def _last_invoice_date_calc (self, cr, uid, ids, name, arg, context={}):
228 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
230 cr.execute ("select account_analytic_line.account_id, \
231 date(max(account_invoice.date_invoice)) \
232 from account_analytic_line \
233 join account_invoice \
234 on account_analytic_line.invoice_id = account_invoice.id \
235 where account_analytic_line.account_id IN %s \
236 and account_analytic_line.invoice_id is not null \
237 GROUP BY account_analytic_line.account_id",(parent_ids,))
238 for account_id, sum in cr.fetchall():
239 res[account_id] = sum
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, '')
247 res[id] = res.get(id, '')
250 def _last_worked_invoiced_date_calc (self, cr, uid, ids, name, arg, context={}):
252 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
254 cr.execute("select account_analytic_line.account_id, max(date) \
255 from account_analytic_line \
256 where account_id IN %s \
257 and invoice_id is not null \
258 GROUP BY account_analytic_line.account_id;",(parent_ids,))
259 for account_id, sum in cr.fetchall():
260 res[account_id] = sum
262 res.setdefault(obj_id, '')
263 for child_id in self.search(cr, uid,
264 [('parent_id', 'child_of', [obj_id])]):
265 if res[obj_id] < res.get(child_id, ''):
266 res[obj_id] = res.get(child_id, '')
268 res[id] = res.get(id, '')
271 def _remaining_hours_calc(self, cr, uid, ids, name, arg, context={}):
273 for account in self.browse(cr, uid, ids):
274 if account.quantity_max != 0:
275 res[account.id] = account.quantity_max - account.hours_quantity
279 res[id] = round(res.get(id, 0.0),2)
282 def _hours_qtt_invoiced_calc(self, cr, uid, ids, name, arg, context={}):
284 for account in self.browse(cr, uid, ids):
285 res[account.id] = account.hours_quantity - account.hours_qtt_non_invoiced
286 if res[account.id] < 0:
289 res[id] = round(res.get(id, 0.0),2)
292 def _revenue_per_hour_calc(self, cr, uid, ids, name, arg, context={}):
294 for account in self.browse(cr, uid, ids):
295 if account.hours_qtt_invoiced == 0:
298 res[account.id] = account.ca_invoiced / account.hours_qtt_invoiced
300 res[id] = round(res.get(id, 0.0),2)
303 def _real_margin_rate_calc(self, cr, uid, ids, name, arg, context={}):
305 for account in self.browse(cr, uid, ids):
306 if account.ca_invoiced == 0:
308 elif account.total_cost != 0.0:
309 res[account.id] = -(account.real_margin / account.total_cost) * 100
311 res[account.id] = 0.0
313 res[id] = round(res.get(id, 0.0),2)
316 def _remaining_ca_calc(self, cr, uid, ids, name, arg, context={}):
318 for account in self.browse(cr, uid, ids):
319 if account.amount_max != 0:
320 res[account.id] = account.amount_max - account.ca_invoiced
324 res[id] = round(res.get(id, 0.0),2)
327 def _real_margin_calc(self, cr, uid, ids, name, arg, context={}):
329 for account in self.browse(cr, uid, ids):
330 res[account.id] = account.ca_invoiced + account.total_cost
332 res[id] = round(res.get(id, 0.0),2)
335 def _theorical_margin_calc(self, cr, uid, ids, name, arg, context={}):
337 for account in self.browse(cr, uid, ids):
338 res[account.id] = account.ca_theorical + account.total_cost
340 res[id] = round(res.get(id, 0.0),2)
343 def _month(self, cr, uid, ids, name, arg, context=None):
346 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
348 cr.execute('SELECT DISTINCT(month_id) FROM account_analytic_analysis_summary_month ' \
349 'WHERE account_id IN %s AND unit_amount <> 0.0',(parent_ids,))
350 res[id] = [int(id * 1000000 + int(x[0])) for x in cr.fetchall()]
355 def _user(self, cr, uid, ids, name, arg, context=None):
357 cr.execute('SELECT MAX(id) FROM res_users')
358 max_user = cr.fetchone()[0]
360 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', ids)]))
362 cr.execute('SELECT DISTINCT("user") FROM account_analytic_analysis_summary_user ' \
363 'WHERE account_id IN %s AND unit_amount <> 0.0',(parent_ids,))
364 res[id] = [int((id * max_user) + x[0]) for x in cr.fetchall()]
370 'ca_invoiced': fields.function(_ca_invoiced_calc, method=True, type='float', string='Invoiced Amount', help="Total customer invoiced amount for this account."),
371 '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."),
372 '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."),
373 '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."),
374 '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'."),
375 '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."),
376 '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."),
377 '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."),
378 '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."),
379 '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."),
380 'remaining_hours': fields.function(_remaining_hours_calc, method=True, type='float', string='Remaining Hours', help="Computed using the formula: Maximum Quantity - Hours Tot."),
381 'remaining_ca': fields.function(_remaining_ca_calc, method=True, type='float', string='Remaining Revenue', help="Computed using the formula: Max Invoice Price - Invoiced Amount."),
382 '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."),
383 'real_margin': fields.function(_real_margin_calc, method=True, type='float', string='Real Margin', help="Computed using the formula: Invoiced Amount - Total Costs."),
384 'theorical_margin': fields.function(_theorical_margin_calc, method=True, type='float', string='Theorical Margin', help="Computed using the formula: Theorial Revenue - Total Costs"),
385 '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."),
386 'month_ids': fields.function(_month, method=True, type='many2many', relation='account_analytic_analysis.summary.month', string='Month'),
387 'user_ids': fields.function(_user, method=True, type="many2many", relation='account_analytic_analysis.summary.user', string='User'),
389 account_analytic_account()
391 class account_analytic_account_summary_user(osv.osv):
392 _name = "account_analytic_analysis.summary.user"
393 _description = "Hours Summary by User"
398 def _unit_amount(self, cr, uid, ids, name, arg, context=None):
400 account_obj = self.pool.get('account.analytic.account')
401 cr.execute('SELECT MAX(id) FROM res_users')
402 max_user = cr.fetchone()[0]
403 account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids]
404 user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids]
405 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', account_ids)]))
407 cr.execute('SELECT id, unit_amount ' \
408 'FROM account_analytic_analysis_summary_user ' \
409 'WHERE account_id IN %s ' \
410 'AND "user" IN %s',(parent_ids, user_ids,))
411 for sum_id, unit_amount in cr.fetchall():
412 res[sum_id] = unit_amount
414 res.setdefault(obj_id, 0.0)
415 for child_id in account_obj.search(cr, uid,
416 [('parent_id', 'child_of', [int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)))])]):
417 if child_id != int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0))):
418 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)
420 res[id] = round(res.get(id, 0.0), 2)
424 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
425 'unit_amount': fields.function(_unit_amount, method=True, type='float',
426 string='Total Time'),
427 'user' : fields.many2one('res.users', 'User'),
430 tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_user')
431 cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (' \
433 '(u.account_id * u.max_user) + u."user" AS id, ' \
434 'u.account_id AS account_id, ' \
435 'u."user" AS "user", ' \
436 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
439 'a.id AS account_id, ' \
440 'u1.id AS "user", ' \
441 'MAX(u2.id) AS max_user ' \
443 'res_users AS u1, ' \
444 'res_users AS u2, ' \
445 'account_analytic_account AS a ' \
446 'GROUP BY u1.id, a.id ' \
450 'l.account_id AS account_id, ' \
451 'l.user_id AS "user", ' \
452 'SUM(l.unit_amount) AS unit_amount ' \
453 'FROM account_analytic_line AS l, ' \
454 'account_analytic_journal AS j ' \
455 'WHERE (j.type = \'general\') and (j.id=l.journal_id) ' \
456 'GROUP BY l.account_id, l.user_id ' \
459 'u.account_id = l.account_id ' \
460 'AND u."user" = l."user"' \
462 'GROUP BY u."user", u.account_id, u.max_user' \
465 def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
472 fields = self._columns.keys()
474 # construct a clause for the rules :
475 d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
477 # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
478 fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
481 cr.execute('SELECT MAX(id) FROM res_users')
482 max_user = cr.fetchone()[0]
484 fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
485 for i in range(0, len(ids), cr.IN_MAX):
486 sub_ids = ids[i:i+cr.IN_MAX]
488 cr.execute('select %s from \"%s\" where id in (%s) ' \
489 'and account_id in (%s) ' \
490 'and "user" in (%s) and %s order by %s' % \
491 (','.join(fields_pre2 + ['id']), self._table,
492 ','.join([str(x) for x in sub_ids]),
493 ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
494 ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), d1,
496 if not cr.rowcount == len({}.fromkeys(sub_ids)):
497 raise except_orm(_('AccessError'),
498 _('You try to bypass an access rule (Document type: %s).') % self._description)
500 cr.execute('select %s from \"%s\" where id in (%s) ' \
501 'and account_id in (%s) ' \
502 'and "user" in (%s) order by %s' % \
503 (','.join(fields_pre2 + ['id']), self._table,
504 ','.join([str(x) for x in sub_ids]),
505 ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
506 ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]),
508 res.extend(cr.dictfetchall())
510 res = map(lambda x: {'id': x}, ids)
513 if self._columns[f].translate:
514 ids = map(lambda x: x['id'], res)
515 res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
517 r[f] = res_trans.get(r['id'], False) or r[f]
519 for table in self._inherits:
520 col = self._inherits[table]
521 cols = intersect(self._inherit_fields.keys(), fields)
524 res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
532 record.update(res3[record[col]])
533 if col not in fields:
536 # all fields which need to be post-processed by a simple function (symbol_get)
537 fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
539 # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
540 # to get the _symbol_get in each occurence
542 for f in fields_post:
543 r[f] = self.columns[f]._symbol_get(r[f])
544 ids = map(lambda x: x['id'], res)
546 # all non inherited fields for which the attribute whose name is in load is False
547 fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
548 for f in fields_post:
549 # get the value of that field for all records/ids
550 res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
552 record[f] = res2[record['id']]
556 account_analytic_account_summary_user()
558 class account_analytic_account_summary_month(osv.osv):
559 _name = "account_analytic_analysis.summary.month"
560 _description = "Hours summary by month"
565 def _unit_amount(self, cr, uid, ids, name, arg, context=None):
567 account_obj = self.pool.get('account.analytic.account')
568 account_ids = [int(str(int(x))[:-6]) for x in ids]
569 month_ids = [int(str(int(x))[-6:]) for x in ids]
570 parent_ids = tuple(self.search(cr, uid, [('parent_id', 'child_of', account_ids)]))
572 cr.execute('SELECT id, unit_amount ' \
573 'FROM account_analytic_analysis_summary_month ' \
574 'WHERE account_id IN %s ' \
575 'AND month_id IN %s ',(parent_ids, month_ids,))
576 for sum_id, unit_amount in cr.fetchall():
577 res[sum_id] = unit_amount
579 res.setdefault(obj_id, 0.0)
580 for child_id in account_obj.search(cr, uid,
581 [('parent_id', 'child_of', [int(str(int(obj_id))[:-6])])]):
582 if child_id != int(str(int(obj_id))[:-6]):
583 res[obj_id] += res.get(int(child_id * 1000000 + int(str(int(obj_id))[-6:])), 0.0)
585 res[id] = round(res.get(id, 0.0), 2)
589 'account_id': fields.many2one('account.analytic.account', 'Analytic Account',
591 'unit_amount': fields.function(_unit_amount, method=True, type='float',
592 string='Total Time'),
593 'month': fields.char('Month', size=25, readonly=True),
597 tools.sql.drop_view_if_exists(cr, 'account_analytic_analysis_summary_month')
598 cr.execute('CREATE VIEW account_analytic_analysis_summary_month AS (' \
600 '(TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') + (d.account_id * 1000000))::integer AS id, ' \
601 'd.account_id AS account_id, ' \
602 'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \
603 'TO_NUMBER(TO_CHAR(d.month, \'YYYYMM\'), \'999999\') AS month_id, ' \
604 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
611 'a.id AS account_id, ' \
612 'l.month AS month ' \
615 'DATE_TRUNC(\'month\', l.date) AS month ' \
616 'FROM account_analytic_line AS l, ' \
617 'account_analytic_journal AS j ' \
618 'WHERE j.type = \'general\' ' \
619 'GROUP BY DATE_TRUNC(\'month\', l.date) ' \
621 'account_analytic_account AS a ' \
622 'GROUP BY l.month, a.id ' \
624 'GROUP BY d2.account_id, d2.month ' \
628 'l.account_id AS account_id, ' \
629 'DATE_TRUNC(\'month\', l.date) AS month, ' \
630 'SUM(l.unit_amount) AS unit_amount ' \
631 'FROM account_analytic_line AS l, ' \
632 'account_analytic_journal AS j ' \
633 'WHERE (j.type = \'general\') and (j.id=l.journal_id) ' \
634 'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \
637 'd.account_id = l.account_id ' \
638 'AND d.month = l.month' \
640 'GROUP BY d.month, d.account_id ' \
643 def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
650 fields = self._columns.keys()
652 # construct a clause for the rules :
653 d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
655 # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
656 fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
660 fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
661 for i in range(0, len(ids), cr.IN_MAX):
662 sub_ids = ids[i:i+cr.IN_MAX]
664 cr.execute('select %s from \"%s\" where id in (%s) ' \
665 'and account_id in (%s) ' \
666 'and month_id in (%s) and %s order by %s' % \
667 (','.join(fields_pre2 + ['id']), self._table,
668 ','.join([str(x) for x in sub_ids]),
669 ','.join([str(x)[:-6] for x in sub_ids]),
670 ','.join([str(x)[-6:] for x in sub_ids]), d1,
672 if not cr.rowcount == len({}.fromkeys(sub_ids)):
673 raise except_orm(_('AccessError'),
674 _('You try to bypass an access rule (Document type: %s).') % self._description)
676 cr.execute('select %s from \"%s\" where id in (%s) ' \
677 'and account_id in (%s) ' \
678 'and month_id in (%s) order by %s' % \
679 (','.join(fields_pre2 + ['id']), self._table,
680 ','.join([str(x) for x in sub_ids]),
681 ','.join([str(x)[:-6] for x in sub_ids]),
682 ','.join([str(x)[-6:] for x in sub_ids]),
684 res.extend(cr.dictfetchall())
686 res = map(lambda x: {'id': x}, ids)
689 if self._columns[f].translate:
690 ids = map(lambda x: x['id'], res)
691 res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
693 r[f] = res_trans.get(r['id'], False) or r[f]
695 for table in self._inherits:
696 col = self._inherits[table]
697 cols = intersect(self._inherit_fields.keys(), fields)
700 res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
708 record.update(res3[record[col]])
709 if col not in fields:
712 # all fields which need to be post-processed by a simple function (symbol_get)
713 fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
715 # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
716 # to get the _symbol_get in each occurence
718 for f in fields_post:
719 r[f] = self.columns[f]._symbol_get(r[f])
720 ids = map(lambda x: x['id'], res)
722 # all non inherited fields for which the attribute whose name is in load is False
723 fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
724 for f in fields_post:
725 # get the value of that field for all records/ids
726 res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
728 record[f] = res2[record['id']]
732 account_analytic_account_summary_month()
735 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: