283 lines
12 KiB
283 lines
12 KiB
from openerp.osv import osv,fields as old_fields
|
|
from openerp import api, models, fields, tools
|
|
from openerp.tools.safe_eval import safe_eval
|
|
try:
|
|
from openerp.addons.email_template.email_template import mako_template_env
|
|
except ImportError:
|
|
try:
|
|
from openerp.addons.mail.mail_template import mako_template_env
|
|
except ImportError:
|
|
pass
|
|
|
|
import copy
|
|
from openerp.tools.translate import _
|
|
from datetime import date, datetime, timedelta
|
|
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
|
|
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
|
|
|
class mail_wall_widgets_widget(models.Model):
|
|
_name = 'mail.wall.widgets.widget'
|
|
_order = "sequence, id"
|
|
|
|
_columns = {
|
|
'name': old_fields.char('Name', required=True, translate=True),
|
|
'type': old_fields.selection(string='Type', selection=[
|
|
('list', 'List'),
|
|
('funnel', 'Funnel'),
|
|
('slice', 'Slice'),
|
|
#('', ''),
|
|
#('', ''),
|
|
#('', ''),
|
|
#('', ''),
|
|
], help='''
|
|
Slice - use "domain" for total and "won_domain" for target
|
|
'''),
|
|
'description': old_fields.text('Description', translate=True),
|
|
'group_ids': old_fields.many2many('res.groups', relation='mail_wall_widgets_widget_group', column1='widget_id', column2='group_id', string='Groups', help="User groups to show widget"),
|
|
'model_id': old_fields.many2one('ir.model', string='Model', help='The model object for the field to evaluate'),
|
|
'domain': old_fields.char("Filter Domain", help="Domain for filtering records. General rule, not user depending, e.g. [('state', '=', 'done')]. The expression can contain reference to 'user' which is a browse record of the current user if not in batch mode.", required=True),
|
|
'limit': old_fields.integer('Limit', help='Limit count of records to show'),
|
|
'order': old_fields.char('Order', help='Order of records to show'),
|
|
'value_field_id': old_fields.many2one('ir.model.fields',
|
|
string='Value field',
|
|
help='The field containing the value of record'),
|
|
'stage_field_id': old_fields.many2one('ir.model.fields',
|
|
string='Stage field',
|
|
help='Field to split records in funnel. It can be selection type or many2one (the later should have "sequence" field)'),
|
|
#'stage_field_domain': old_fields.many2one('ir.model.fields',
|
|
# string='Stage field domain',
|
|
# help='(for many2one stage_field_id) Domain to find stage objects'),
|
|
'won_domain': old_fields.char('Won domain',
|
|
help='Domain to find won objects'),
|
|
'field_date_id': old_fields.many2one('ir.model.fields',
|
|
string='Date Field',
|
|
help='The date to use for the time period evaluated'),
|
|
'start_date': old_fields.date('Start Date'),
|
|
'end_date': old_fields.date('End Date'), # no start and end = always active
|
|
'content': old_fields.char('Line template', help='Mako template to show content'),
|
|
'value_field_monetary': old_fields.boolean('Value is monetary'),
|
|
'cache': old_fields.boolean('Cache'),
|
|
'active': old_fields.boolean('Active'),
|
|
'sequence': old_fields.integer('Sequence', help='Sequence number for ordering'),
|
|
}
|
|
precision = fields.Float('Precision', help='round(Value/precision) * precision. E.g. 12345,333333 will be rounded to 12345,33 for precision=0.01, and to 12000 for precision=1000', default=0.01)
|
|
agenda = fields.Boolean('Agenda', help='Split records by date: overdue, today, tomorrow, later')
|
|
_defaults = {
|
|
'active': True,
|
|
'cache': False,
|
|
'limit': None,
|
|
'order': None,
|
|
}
|
|
|
|
@api.one
|
|
def get_data(self, user):
|
|
|
|
domain = safe_eval(self.domain, {'user': user})
|
|
won_domain = safe_eval(self.won_domain or '[]', {'user': user})
|
|
|
|
field_date_name = self.field_date_id and self.field_date_id.name
|
|
if self.start_date and field_date_name:
|
|
domain.append((field_date_name, '>=', self.start_date))
|
|
if self.end_date and field_date_name:
|
|
domain.append((field_date_name, '<=', self.end_date))
|
|
|
|
res = {
|
|
'name': self.name,
|
|
'type': self.type,
|
|
'model': self.model_id.model,
|
|
'domain': str(domain),
|
|
'precision': self.precision,
|
|
}
|
|
obj = self.env[self.model_id.model]
|
|
if self.type == 'list':
|
|
total_count = obj.search_count(domain)
|
|
groups = [{'test': lambda r: True}]
|
|
if self.agenda:
|
|
today = date.today()
|
|
tomorrow = today + timedelta(days=1)
|
|
def r2date(r):
|
|
d = getattr(r, field_date_name)
|
|
if d:
|
|
d = datetime.strptime(d, self.field_date_id.ttype=='date' and DEFAULT_SERVER_DATE_FORMAT or DEFAULT_SERVER_DATETIME_FORMAT)
|
|
d = d.date()
|
|
else:
|
|
d = date.today()
|
|
return d
|
|
groups = [
|
|
{
|
|
'label': _('Overdue'),
|
|
'class': 'overdue',
|
|
'test': lambda r: r2date(r) < today,
|
|
'mandatory': False,
|
|
},
|
|
{
|
|
'label': _('Today'),
|
|
'class': 'today',
|
|
'test': lambda r: r2date(r) == today,
|
|
'mandatory': True,
|
|
},
|
|
{
|
|
'label': _('Tomorrow'),
|
|
'class': 'tomorrow',
|
|
'test': lambda r: r2date(r) == tomorrow,
|
|
'mandatory': False,
|
|
},
|
|
{
|
|
'label': _('Later'),
|
|
'class': 'later',
|
|
'test': lambda r: r2date(r) > tomorrow,
|
|
'mandatory': False,
|
|
},
|
|
]
|
|
for g in groups:
|
|
g['lines'] = []
|
|
|
|
res.update({
|
|
'more': self.limit and self.limit < total_count,
|
|
'total_count': total_count,
|
|
'agenda': self.agenda,
|
|
'groups': groups,
|
|
})
|
|
for r in obj.search(domain, limit=self.limit, order=self.order):
|
|
mako = mako_template_env.from_string(tools.ustr(self.content))
|
|
content = mako.render({'record':r})
|
|
r_json = {
|
|
'id': r.id,
|
|
#'fields': dict( (f,getattr(r,f)) for f in fields),
|
|
'display_mode': 'progress',
|
|
'state': 'inprogress',
|
|
'completeness': 0,
|
|
'name': content,
|
|
'description': '',
|
|
}
|
|
if self.value_field_id:
|
|
r_json['current'] = getattr(r, self.value_field_id.name)
|
|
if self.value_field_monetary:
|
|
r_json['monetary'] = 1
|
|
for g in groups:
|
|
if g['test'](r):
|
|
g['lines'].append(r_json)
|
|
break
|
|
for g in groups:
|
|
del g['test']
|
|
elif self.type == 'funnel':
|
|
stage_ids = [] # [key]
|
|
for group in obj.read_group(domain, [], [self.stage_field_id.name]):
|
|
key = group[self.stage_field_id.name]
|
|
if isinstance(key, (list, tuple)):
|
|
key = key[0]
|
|
stage_ids.append(key)
|
|
|
|
stages = [] # [{'name':Name, 'id': key}]
|
|
if self.stage_field_id.ttype == 'selection':
|
|
d = dict (self.stage_field_id.selection)
|
|
stages = [ {'id':id, 'name':d[id]} for id in stage_ids ]
|
|
else: # many2one
|
|
stage_model = self.stage_field_id.relation
|
|
for r in self.env[stage_model].browse(stage_ids):
|
|
stages.append({'id': r.id, 'name':r.name_get()[0][1]})
|
|
|
|
value_field_name = self.value_field_id.name
|
|
for stage in stages:
|
|
d = copy.copy(domain)
|
|
d.append( (self.stage_field_id.name, '=', stage['id']) )
|
|
result = obj.read_group(d, [value_field_name], [])
|
|
stage['closed_value'] = result and result[0][value_field_name] or 0.0
|
|
stage['domain'] = str(d)
|
|
|
|
# won value
|
|
d = domain + won_domain
|
|
result = obj.read_group(domain, [value_field_name], [])
|
|
won = {'name': _('Won'),
|
|
'id':'__won__',
|
|
'closed_value': result and result[0][value_field_name] or 0.0
|
|
}
|
|
stages.append(won)
|
|
cur = 0
|
|
for stage in reversed(stages):
|
|
cur += stage['closed_value']
|
|
stage['abs_value'] = cur
|
|
total_value = stages[0]['abs_value']
|
|
precision = self.precision
|
|
for s in stages:
|
|
s['rel_value'] = round(100*s['abs_value']/total_value/precision)*precision if total_value else 100
|
|
# dummy fields
|
|
s['display_mode'] = 'progress'
|
|
s['monetary'] = 1
|
|
|
|
res['stages'] = stages
|
|
res['won'] = won
|
|
res['conversion_rate'] = stages[-1]['rel_value']
|
|
elif self.type == 'slice':
|
|
value_field_name = self.value_field_id.name
|
|
for f,d in [('total', domain), ('won', won_domain)]:
|
|
result = obj.read_group(d, [value_field_name], [])
|
|
res[f] = result and result[0][value_field_name] or 0.0
|
|
|
|
res['domain'] = str(domain)
|
|
res['won_domain'] = str(won_domain)
|
|
|
|
precision = self.precision
|
|
total_value = res['total']
|
|
res['slice'] = round(100*res['won']/res['total']/precision)*precision if res['total'] else 100
|
|
# dummy fields
|
|
res['display_mode'] = 'progress'
|
|
res['monetary'] = self.value_field_monetary
|
|
return res
|
|
|
|
class mail_wall_widgets_cache(models.Model):
|
|
_name = 'mail.wall.widgets.cache'
|
|
|
|
cache = fields.Text('Cached data')
|
|
res_id = fields.Integer('Resource ID')
|
|
res_model = fields.Integer('Resource Model')
|
|
user_id = fields.Many2one('res.users')
|
|
|
|
class res_users(models.Model):
|
|
_inherit = 'res.users'
|
|
|
|
@api.v7
|
|
def get_serialised_mail_wall_widgets_summary(self, cr, uid, excluded_categories=None, context=None):
|
|
return self._get_serialised_mail_wall_widgets_summary(cr, uid, uid, excluded_categories=excluded_categories, context=context)[0]
|
|
|
|
@api.one
|
|
def _get_serialised_mail_wall_widgets_summary(self, excluded_categories=None):
|
|
"""
|
|
[
|
|
{
|
|
'id': ...,
|
|
'model': ...,
|
|
'currency': <res.currency id>,
|
|
'data': (depend on model)
|
|
},
|
|
]
|
|
"""
|
|
user = self.env.user
|
|
res = []
|
|
model = 'mail.wall.widgets.widget'
|
|
domain = [('group_ids', 'in', user.groups_id.ids), ('active', '=', True)]
|
|
for widget in self.env[model].search(domain, order='sequence'):
|
|
if widget.cache:
|
|
#TODO
|
|
continue
|
|
res.append({
|
|
'model': model,
|
|
'id': widget.id,
|
|
'currency': user.company_id.currency_id.id,
|
|
'data': widget.get_data(user)[0],
|
|
})
|
|
return res
|
|
|
|
#def get_challenge_suggestions(self, cr, uid, context=None):
|
|
# """Return the list of challenges suggested to the user"""
|
|
# challenge_info = []
|
|
# challenge_obj = self.pool.get('mail_wall_widgets.challenge')
|
|
# challenge_ids = challenge_obj.search(cr, uid, [('invited_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
|
|
# for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
|
|
# values = {
|
|
# 'id': challenge.id,
|
|
# 'name': challenge.name,
|
|
# 'description': challenge.description,
|
|
# }
|
|
# challenge_info.append(values)
|
|
# return challenge_info
|