From e3a7c58182bad4d721485ebbf6de496b40791b41 Mon Sep 17 00:00:00 2001
From: ced <>
Date: Thu, 20 Dec 2007 15:38:53 +0000
Subject: [PATCH] Improve summary object to take care of child
bzr revid: ced-24c1b4ad1ac08040ff9f015f5e64a4c5337a0f57
---
.../account_analytic_analysis.py | 404 +++++++++++++++---
.../account_analytic_analysis_view.xml | 6 +-
2 files changed, 356 insertions(+), 54 deletions(-)
diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py
index 295e625bdfc..1d07444961f 100644
--- a/addons/account_analytic_analysis/account_analytic_analysis.py
+++ b/addons/account_analytic_analysis/account_analytic_analysis.py
@@ -28,6 +28,8 @@
##############################################################################
import operator
from osv import osv, fields
+from osv.orm import ID_MAX
+
class account_analytic_account(osv.osv):
_name = "account.analytic.account"
@@ -382,6 +384,28 @@ class account_analytic_account(osv.osv):
res[id] = round(res.get(id, 0.0),2)
return res
+ def _month(self, cr, uid, ids, name, arg, context=None):
+ res = {}
+ for id in ids:
+ ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])])
+ cr.execute('SELECT month_id FROM account_analytic_analysis_summary_month ' \
+ 'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \
+ 'AND unit_amount <> 0.0')
+ res[id] = [int(str(x[0]) + str(id)) for x in cr.fetchall()]
+ return res
+
+ def _user(self, cr, uid, ids, name, arg, context=None):
+ res = {}
+ cr.execute('SELECT MAX(id) FROM res_users')
+ max_user = cr.fetchone()[0]
+ for id in ids:
+ ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])])
+ cr.execute('SELECT "user" FROM account_analytic_analysis_summary_user ' \
+ 'WHERE account_id in (' + ','.join([str(x) for x in ids2]) + ') ' \
+ 'AND unit_amount <> 0.0')
+ res[id] = [int((id * max_user) + x[0]) for x in cr.fetchall()]
+ return res
+
_columns ={
'ca_invoiced': fields.function(_ca_invoiced_calc, method=True, type='float', string='Invoiced amount'),
'total_cost': fields.function(_total_cost_calc, method=True, type='float', string='Total cost'),
@@ -399,76 +423,354 @@ class account_analytic_account(osv.osv):
'real_margin': fields.function(_real_margin_calc, method=True, type='float', string='Real margin'),
'theorical_margin': fields.function(_theorical_margin_calc, method=True, type='float', string='Theorical margin'),
'real_margin_rate': fields.function(_real_margin_rate_calc, method=True, type='float', string='Real margin rate (%)'),
- 'month_ids': fields.one2many('account_analytic_analysis.summary.month', 'account_id', 'Month', readonly=True),
- 'user_ids': fields.one2many('account_analytic_analysis.summary.user', 'account_id', 'User', readonly=True),
+ 'month_ids': fields.function(_month, method=True, type='many2many', relation='account_analytic_analysis.summary.month', string='Month'),
+ 'user_ids': fields.function(_user, method=True, type="many2many", relation='account_analytic_analysis.summary.user', string='User'),
}
account_analytic_account()
class account_analytic_account_summary_user(osv.osv):
_name = "account_analytic_analysis.summary.user"
_description = "Hours summary by user"
- _order='name'
+ _order='user'
_auto = False
+ _rec_name = 'user'
+
+ def _unit_amount(self, cr, uid, ids, name, arg, context=None):
+ res = {}
+ account_obj = self.pool.get('account.analytic.account')
+ cr.execute('SELECT MAX(id) FROM res_users')
+ max_user = cr.fetchone()[0]
+ account_ids = [int(str(x/max_user - (x%max_user == 0 and 1 or 0))) for x in ids]
+ user_ids = [int(str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user))) for x in ids]
+ account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)])
+ user_set = ','.join([str(x) for x in user_ids])
+ if account_ids2:
+ acc_set = ','.join([str(x) for x in account_ids2])
+ cr.execute('SELECT id, unit_amount ' \
+ 'FROM account_analytic_analysis_summary_user ' \
+ 'WHERE account_id in (%s) ' \
+ 'AND "user" in (%s) ' % \
+ (acc_set, user_set))
+ for sum_id, unit_amount in cr.fetchall():
+ res[sum_id] = unit_amount
+ for obj_id in ids:
+ res.setdefault(obj_id, 0.0)
+ for child_id in account_obj.search(cr, uid,
+ [('parent_id', 'child_of', [int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0)))])]):
+ if child_id != int(str(obj_id/max_user - (obj_id%max_user == 0 and 1 or 0))):
+ 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)
+ for id in ids:
+ res[id] = round(res.get(id, 0.0), 2)
+ return res
+
_columns = {
'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
- 'unit_amount': fields.float('Total Time', digits=(16,2), readonly=True),
- 'name' : fields.many2one('res.users','User'),
+ 'unit_amount': fields.function(_unit_amount, method=True, type='float',
+ string='Total Time'),
+ 'user' : fields.many2one('res.users', 'User'),
}
def init(self, cr):
- cr.execute("""
- create or replace view account_analytic_analysis_summary_user as (
- select
- id,
- unit_amount,
- account_id,
- name from (
- select
- min(account_analytic_line.id) as id,
- user_id as name,
- account_id,
- sum(unit_amount) as unit_amount
- from
- account_analytic_line
- join
- account_analytic_journal on account_analytic_line.journal_id = account_analytic_journal.id
- where
- account_analytic_journal.type = 'general'
- group by
- account_id, user_id
- order by
- user_id,account_id asc )as
- sous_account_analytic_analysis_summary_user
- order by
- name desc,account_id)""")
+ cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_user AS (' \
+ 'SELECT ' \
+ '(u.account_id * u.max_user) + u.user AS id, ' \
+ 'u.account_id AS account_id, ' \
+ 'u.user AS user, ' \
+ 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
+ 'FROM ' \
+ '(SELECT ' \
+ 'a.id AS account_id, ' \
+ 'u1.id AS user, ' \
+ 'MAX(u2.id) AS max_user ' \
+ 'FROM ' \
+ 'res_users AS u1, ' \
+ 'res_users AS u2, ' \
+ 'account_analytic_account AS a ' \
+ 'GROUP BY u1.id, a.id ' \
+ ') AS u ' \
+ 'LEFT JOIN ' \
+ '(SELECT ' \
+ 'l.account_id AS account_id, ' \
+ 'l.user_id AS user, ' \
+ 'SUM(l.unit_amount) AS unit_amount ' \
+ 'FROM account_analytic_line AS l, ' \
+ 'account_analytic_journal AS j ' \
+ 'WHERE j.type = \'general\' ' \
+ 'GROUP BY l.account_id, l.user_id ' \
+ ') AS l '
+ 'ON (' \
+ 'u.account_id = l.account_id ' \
+ 'AND u.user = l.user' \
+ ') ' \
+ 'GROUP BY u.user, u.account_id, u.max_user' \
+ ')')
+
+ def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
+ if not context:
+ context={}
+ if not ids:
+ return []
+
+ if fields==None:
+ fields = self._columns.keys()
+
+ # construct a clause for the rules :
+ d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
+
+ # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
+ fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
+
+ res = []
+ cr.execute('SELECT MAX(id) FROM res_users')
+ max_user = cr.fetchone()[0]
+ if len(fields_pre) :
+ fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
+ for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)):
+ sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
+ if d1:
+ cr.execute('select %s from \"%s\" where id in (%s) ' \
+ 'and account_id in (%s) ' \
+ 'and "user" in (%s) and %s order by %s' % \
+ (','.join(fields_pre2 + ['id']), self._table,
+ ','.join([str(x) for x in sub_ids]),
+ ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
+ ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]), d1,
+ self._order),d2)
+ if not cr.rowcount == len({}.fromkeys(sub_ids)):
+ raise except_orm('AccessError',
+ 'You try to bypass an access rule (Document type: %s).' % self._description)
+ else:
+ cr.execute('select %s from \"%s\" where id in (%s) ' \
+ 'and account_id in (%s) ' \
+ 'and "user" in (%s) order by %s' % \
+ (','.join(fields_pre2 + ['id']), self._table,
+ ','.join([str(x) for x in sub_ids]),
+ ','.join([str(x/max_user - (x%max_user == 0 and 1 or 0)) for x in sub_ids]),
+ ','.join([str(x-((x/max_user - (x%max_user == 0 and 1 or 0)) *max_user)) for x in sub_ids]),
+ self._order))
+ res.extend(cr.dictfetchall())
+ else:
+ res = map(lambda x: {'id': x}, ids)
+
+ for f in fields_pre:
+ if self._columns[f].translate:
+ ids = map(lambda x: x['id'], res)
+ res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
+ for r in res:
+ r[f] = res_trans.get(r['id'], False) or r[f]
+
+ for table in self._inherits:
+ col = self._inherits[table]
+ cols = intersect(self._inherit_fields.keys(), fields)
+ if not cols:
+ continue
+ res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
+
+ res3 = {}
+ for r in res2:
+ res3[r['id']] = r
+ del r['id']
+
+ for record in res:
+ record.update(res3[record[col]])
+ if col not in fields:
+ del record[col]
+
+ # all fields which need to be post-processed by a simple function (symbol_get)
+ fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
+ if fields_post:
+ # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
+ # to get the _symbol_get in each occurence
+ for r in res:
+ for f in fields_post:
+ r[f] = self.columns[f]._symbol_get(r[f])
+ ids = map(lambda x: x['id'], res)
+
+ # all non inherited fields for which the attribute whose name is in load is False
+ fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
+ for f in fields_post:
+ # get the value of that field for all records/ids
+ res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
+ for record in res:
+ record[f] = res2[record['id']]
+
+ return res
+
account_analytic_account_summary_user()
class account_analytic_account_summary_month(osv.osv):
_name = "account_analytic_analysis.summary.month"
_description = "Hours summary by month"
_auto = False
+ _rec_name = 'month'
+ _order = 'month'
+
+ def _unit_amount(self, cr, uid, ids, name, arg, context=None):
+ res = {}
+ account_obj = self.pool.get('account.analytic.account')
+ account_ids = [int(str(x)[6:]) for x in ids]
+ month_ids = [int(str(x)[:6]) for x in ids]
+ account_ids2 = account_obj.search(cr, uid, [('parent_id', 'child_of', account_ids)])
+ month_set = ','.join([str(x) for x in month_ids])
+ if account_ids2:
+ acc_set = ','.join([str(x) for x in account_ids2])
+ cr.execute('SELECT id, unit_amount ' \
+ 'FROM account_analytic_analysis_summary_month ' \
+ 'WHERE account_id in (%s) ' \
+ 'AND month_id in (%s) ' % \
+ (acc_set, month_set))
+ for sum_id, unit_amount in cr.fetchall():
+ res[sum_id] = unit_amount
+ for obj_id in ids:
+ res.setdefault(obj_id, 0.0)
+ for child_id in account_obj.search(cr, uid,
+ [('parent_id', 'child_of', [int(str(obj_id)[6:])])]):
+ if child_id != int(str(obj_id)[6:]):
+ res[obj_id] += res.get(str(obj_id)[:6] + str(child_id), 0.0)
+ for id in ids:
+ res[id] = round(res.get(id, 0.0), 2)
+ return res
+
_columns = {
- 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True),
- 'unit_amount': fields.float('Total Time', digits=(16,2), readonly=True),
- 'name': fields.char('Month', size=25, readonly=True),
+ 'account_id': fields.many2one('account.analytic.account', 'Analytic Account',
+ readonly=True),
+ 'unit_amount': fields.function(_unit_amount, method=True, type='float',
+ string='Total Time'),
+ 'month': fields.char('Month', size=25, readonly=True),
}
def init(self, cr):
- cr.execute("""create or replace view account_analytic_analysis_summary_month as (
- select id, unit_amount,account_id, sort_month,month as name from (
- select
- min(account_analytic_line.id) as id,
- date_trunc('month', date) as sort_month,
- account_id,
- to_char(date,'Mon YYYY') as month,
- sum(unit_amount) as unit_amount
- from
- account_analytic_line join account_analytic_journal on account_analytic_line.journal_id = account_analytic_journal.id
- where
- account_analytic_journal.type = 'general'
- group by
- sort_month, month, account_id
- order by
- sort_month,account_id asc
- )as sous_account_analytic_analysis_summary_month order by sort_month,account_id)""")
-
+ cr.execute('CREATE OR REPLACE VIEW account_analytic_analysis_summary_month AS (' \
+ 'SELECT ' \
+ 'TO_CHAR(d.month, \'YYYYMM\') || d.account_id AS id, ' \
+ 'd.account_id AS account_id, ' \
+ 'TO_CHAR(d.month, \'Mon YYYY\') AS month, ' \
+ 'TO_CHAR(d.month, \'YYYYMM\') AS month_id, ' \
+ 'COALESCE(SUM(l.unit_amount), 0.0) AS unit_amount ' \
+ 'FROM ' \
+ '(SELECT ' \
+ 'd2.account_id, ' \
+ 'd2.month ' \
+ 'FROM ' \
+ '(SELECT ' \
+ 'a.id AS account_id, ' \
+ 'l.month AS month ' \
+ 'FROM ' \
+ '(SELECT ' \
+ 'DATE_TRUNC(\'month\', l.date) AS month ' \
+ 'FROM account_analytic_line AS l, ' \
+ 'account_analytic_journal AS j ' \
+ 'WHERE j.type = \'general\' ' \
+ 'GROUP BY DATE_TRUNC(\'month\', l.date) ' \
+ ') AS l, ' \
+ 'account_analytic_account AS a ' \
+ 'GROUP BY l.month, a.id ' \
+ ') AS d2 ' \
+ 'GROUP BY d2.account_id, d2.month ' \
+ ') AS d ' \
+ 'LEFT JOIN ' \
+ '(SELECT ' \
+ 'l.account_id AS account_id, ' \
+ 'DATE_TRUNC(\'month\', l.date) AS month, ' \
+ 'SUM(l.unit_amount) AS unit_amount ' \
+ 'FROM account_analytic_line AS l, ' \
+ 'account_analytic_journal AS j ' \
+ 'WHERE j.type = \'general\' ' \
+ 'GROUP BY l.account_id, DATE_TRUNC(\'month\', l.date) ' \
+ ') AS l '
+ 'ON (' \
+ 'd.account_id = l.account_id ' \
+ 'AND d.month = l.month' \
+ ') ' \
+ 'GROUP BY d.month, d.account_id ' \
+ ')')
+
+ def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'):
+ if not context:
+ context={}
+ if not ids:
+ return []
+
+ if fields==None:
+ fields = self._columns.keys()
+
+ # construct a clause for the rules :
+ d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name)
+
+ # all inherited fields + all non inherited fields for which the attribute whose name is in load is True
+ fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values()
+
+ res = []
+ if len(fields_pre) :
+ fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
+ for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)):
+ sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
+ if d1:
+ cr.execute('select %s from \"%s\" where id in (%s) ' \
+ 'and account_id in (%s) ' \
+ 'and month_id in (%s) and %s order by %s' % \
+ (','.join(fields_pre2 + ['id']), self._table,
+ ','.join([str(x) for x in sub_ids]),
+ ','.join([str(x)[6:] for x in sub_ids]),
+ ','.join([str(x)[:6] for x in sub_ids]), d1,
+ self._order),d2)
+ if not cr.rowcount == len({}.fromkeys(sub_ids)):
+ raise except_orm('AccessError',
+ 'You try to bypass an access rule (Document type: %s).' % self._description)
+ else:
+ cr.execute('select %s from \"%s\" where id in (%s) ' \
+ 'and account_id in (%s) ' \
+ 'and month_id in (%s) order by %s' % \
+ (','.join(fields_pre2 + ['id']), self._table,
+ ','.join([str(x) for x in sub_ids]),
+ ','.join([str(x)[6:] for x in sub_ids]),
+ ','.join([str(x)[:6] for x in sub_ids]),
+ self._order))
+ res.extend(cr.dictfetchall())
+ else:
+ res = map(lambda x: {'id': x}, ids)
+
+ for f in fields_pre:
+ if self._columns[f].translate:
+ ids = map(lambda x: x['id'], res)
+ res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids)
+ for r in res:
+ r[f] = res_trans.get(r['id'], False) or r[f]
+
+ for table in self._inherits:
+ col = self._inherits[table]
+ cols = intersect(self._inherit_fields.keys(), fields)
+ if not cols:
+ continue
+ res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load)
+
+ res3 = {}
+ for r in res2:
+ res3[r['id']] = r
+ del r['id']
+
+ for record in res:
+ record.update(res3[record[col]])
+ if col not in fields:
+ del record[col]
+
+ # all fields which need to be post-processed by a simple function (symbol_get)
+ fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields)
+ if fields_post:
+ # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need
+ # to get the _symbol_get in each occurence
+ for r in res:
+ for f in fields_post:
+ r[f] = self.columns[f]._symbol_get(r[f])
+ ids = map(lambda x: x['id'], res)
+
+ # all non inherited fields for which the attribute whose name is in load is False
+ fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields)
+ for f in fields_post:
+ # get the value of that field for all records/ids
+ res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res)
+ for record in res:
+ record[f] = res2[record['id']]
+
+ return res
+
account_analytic_account_summary_month()
diff --git a/addons/account_analytic_analysis/account_analytic_analysis_view.xml b/addons/account_analytic_analysis/account_analytic_analysis_view.xml
index 9f81735a4f7..742cee91c99 100644
--- a/addons/account_analytic_analysis/account_analytic_analysis_view.xml
+++ b/addons/account_analytic_analysis/account_analytic_analysis_view.xml
@@ -46,15 +46,15 @@
-
+
-
-
+
+