You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
12 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. # -*- coding: utf-8 -*-
  2. from openerp.osv import fields as old_fields
  3. from openerp import api, models, fields, tools
  4. from openerp.tools.safe_eval import safe_eval
  5. try:
  6. from openerp.addons.email_template.email_template import mako_template_env
  7. except ImportError:
  8. try:
  9. from openerp.addons.mail.mail_template import mako_template_env
  10. except ImportError:
  11. pass
  12. import copy
  13. from openerp.tools.translate import _
  14. from datetime import date, datetime, timedelta
  15. from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
  16. from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
  17. class MailWallWidgetsWidget(models.Model):
  18. _name = 'mail.wall.widgets.widget'
  19. _order = "sequence, id"
  20. _columns = {
  21. 'name': old_fields.char('Name', required=True, translate=True),
  22. 'type': old_fields.selection(string='Type', selection=[
  23. ('list', 'List'),
  24. ('funnel', 'Funnel'),
  25. ('slice', 'Slice'),
  26. # ('', ''),
  27. # ('', ''),
  28. # ('', ''),
  29. # ('', ''),
  30. ], help='''
  31. Slice - use "domain" for total and "won_domain" for target
  32. '''),
  33. 'description': old_fields.text('Description', translate=True),
  34. '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"),
  35. 'model_id': old_fields.many2one('ir.model', string='Model', help='The model object for the field to evaluate'),
  36. '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),
  37. 'limit': old_fields.integer('Limit', help='Limit count of records to show'),
  38. 'order': old_fields.char('Order', help='Order of records to show'),
  39. 'value_field_id': old_fields.many2one('ir.model.fields',
  40. string='Value field',
  41. help='The field containing the value of record'),
  42. 'stage_field_id': old_fields.many2one('ir.model.fields',
  43. string='Stage field',
  44. help='Field to split records in funnel. It can be selection type or many2one (the later should have "sequence" field)'),
  45. # 'stage_field_domain': old_fields.many2one('ir.model.fields',
  46. # string='Stage field domain',
  47. # help='(for many2one stage_field_id) Domain to find stage objects'),
  48. 'won_domain': old_fields.char('Won domain',
  49. help='Domain to find won objects'),
  50. 'field_date_id': old_fields.many2one('ir.model.fields',
  51. string='Date Field',
  52. help='The date to use for the time period evaluated'),
  53. 'start_date': old_fields.date('Start Date'),
  54. 'end_date': old_fields.date('End Date'), # no start and end = always active
  55. 'content': old_fields.char('Line template', help='Mako template to show content'),
  56. 'value_field_monetary': old_fields.boolean('Value is monetary'),
  57. 'cache': old_fields.boolean('Cache'),
  58. 'active': old_fields.boolean('Active'),
  59. 'sequence': old_fields.integer('Sequence', help='Sequence number for ordering'),
  60. }
  61. 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)
  62. agenda = fields.Boolean('Agenda', help='Split records by date: overdue, today, tomorrow, later')
  63. _defaults = {
  64. 'active': True,
  65. 'cache': False,
  66. 'limit': None,
  67. 'order': None,
  68. }
  69. @api.one
  70. def get_data(self, user):
  71. domain = safe_eval(self.domain, {'user': user})
  72. won_domain = safe_eval(self.won_domain or '[]', {'user': user})
  73. field_date_name = self.field_date_id and self.field_date_id.name
  74. if self.start_date and field_date_name:
  75. domain.append((field_date_name, '>=', self.start_date))
  76. if self.end_date and field_date_name:
  77. domain.append((field_date_name, '<=', self.end_date))
  78. res = {
  79. 'name': self.name,
  80. 'type': self.type,
  81. 'model': self.model_id.model,
  82. 'domain': str(domain),
  83. 'precision': self.precision,
  84. }
  85. obj = self.env[self.model_id.model]
  86. if self.type == 'list':
  87. total_count = obj.search_count(domain)
  88. groups = [{'test': lambda r: True}]
  89. if self.agenda:
  90. today = date.today()
  91. tomorrow = today + timedelta(days=1)
  92. def r2date(r):
  93. d = getattr(r, field_date_name)
  94. if d:
  95. d = datetime.strptime(d, self.field_date_id.ttype == 'date' and DEFAULT_SERVER_DATE_FORMAT or DEFAULT_SERVER_DATETIME_FORMAT)
  96. d = d.date()
  97. else:
  98. d = date.today()
  99. return d
  100. groups = [
  101. {
  102. 'label': _('Overdue'),
  103. 'class': 'overdue',
  104. 'test': lambda r: r2date(r) < today,
  105. 'mandatory': False,
  106. },
  107. {
  108. 'label': _('Today'),
  109. 'class': 'today',
  110. 'test': lambda r: r2date(r) == today,
  111. 'mandatory': True,
  112. },
  113. {
  114. 'label': _('Tomorrow'),
  115. 'class': 'tomorrow',
  116. 'test': lambda r: r2date(r) == tomorrow,
  117. 'mandatory': False,
  118. },
  119. {
  120. 'label': _('Later'),
  121. 'class': 'later',
  122. 'test': lambda r: r2date(r) > tomorrow,
  123. 'mandatory': False,
  124. },
  125. ]
  126. for g in groups:
  127. g['lines'] = []
  128. res.update({
  129. 'more': self.limit and self.limit < total_count,
  130. 'total_count': total_count,
  131. 'agenda': self.agenda,
  132. 'groups': groups,
  133. })
  134. for r in obj.search(domain, limit=self.limit, order=self.order):
  135. mako = mako_template_env.from_string(tools.ustr(self.content))
  136. content = mako.render({'record': r})
  137. r_json = {
  138. 'id': r.id,
  139. # 'fields': dict( (f,getattr(r,f)) for f in fields),
  140. 'display_mode': 'progress',
  141. 'state': 'inprogress',
  142. 'completeness': 0,
  143. 'name': content,
  144. 'description': '',
  145. }
  146. if self.value_field_id:
  147. r_json['current'] = getattr(r, self.value_field_id.name)
  148. if self.value_field_monetary:
  149. r_json['monetary'] = 1
  150. for g in groups:
  151. if g['test'](r):
  152. g['lines'].append(r_json)
  153. break
  154. for g in groups:
  155. del g['test']
  156. elif self.type == 'funnel':
  157. stage_ids = [] # [key]
  158. for group in obj.read_group(domain, [], [self.stage_field_id.name]):
  159. key = group[self.stage_field_id.name]
  160. if isinstance(key, (list, tuple)):
  161. key = key[0]
  162. stage_ids.append(key)
  163. stages = [] # [{'name':Name, 'id': key}]
  164. if self.stage_field_id.ttype == 'selection':
  165. d = dict(self.stage_field_id.selection)
  166. stages = [{'id': id, 'name': d[id]} for id in stage_ids]
  167. else: # many2one
  168. stage_model = self.stage_field_id.relation
  169. for r in self.env[stage_model].browse(stage_ids):
  170. stages.append({'id': r.id, 'name': r.name_get()[0][1]})
  171. value_field_name = self.value_field_id.name
  172. for stage in stages:
  173. d = copy.copy(domain)
  174. d.append((self.stage_field_id.name, '=', stage['id']))
  175. result = obj.read_group(d, [value_field_name], [])
  176. stage['closed_value'] = result and result[0][value_field_name] or 0.0
  177. stage['domain'] = str(d)
  178. # won value
  179. d = domain + won_domain
  180. result = obj.read_group(domain, [value_field_name], [])
  181. won = {'name': _('Won'),
  182. 'id': '__won__',
  183. 'closed_value': result and result[0][value_field_name] or 0.0
  184. }
  185. stages.append(won)
  186. cur = 0
  187. for stage in reversed(stages):
  188. cur += stage['closed_value']
  189. stage['abs_value'] = cur
  190. total_value = stages[0]['abs_value']
  191. precision = self.precision
  192. for s in stages:
  193. s['rel_value'] = round(100 * s['abs_value'] / total_value / precision) * precision if total_value else 100
  194. # dummy fields
  195. s['display_mode'] = 'progress'
  196. s['monetary'] = 1
  197. res['stages'] = stages
  198. res['won'] = won
  199. res['conversion_rate'] = stages[-1]['rel_value']
  200. elif self.type == 'slice':
  201. value_field_name = self.value_field_id.name
  202. for f, d in [('total', domain), ('won', won_domain)]:
  203. result = obj.read_group(d, [value_field_name], [])
  204. res[f] = result and result[0][value_field_name] or 0.0
  205. res['domain'] = str(domain)
  206. res['won_domain'] = str(won_domain)
  207. precision = self.precision
  208. total_value = res['total']
  209. res['slice'] = round(100 * res['won'] / res['total'] / precision) * precision if res['total'] else 100
  210. # dummy fields
  211. res['display_mode'] = 'progress'
  212. res['monetary'] = self.value_field_monetary
  213. return res
  214. class MailWallWidgetsCache(models.Model):
  215. _name = 'mail.wall.widgets.cache'
  216. cache = fields.Text('Cached data')
  217. res_id = fields.Integer('Resource ID')
  218. res_model = fields.Integer('Resource Model')
  219. user_id = fields.Many2one('res.users')
  220. class ResUsers(models.Model):
  221. _inherit = 'res.users'
  222. @api.v7
  223. def get_serialised_mail_wall_widgets_summary(self, cr, uid, excluded_categories=None, context=None):
  224. return self._get_serialised_mail_wall_widgets_summary(cr, uid, uid, excluded_categories, context=context)[0]
  225. @api.one
  226. def _get_serialised_mail_wall_widgets_summary(self, excluded_categories=None):
  227. """
  228. [
  229. {
  230. 'id': ...,
  231. 'model': ...,
  232. 'currency': <res.currency id>,
  233. 'data': (depend on model)
  234. },
  235. ]
  236. """
  237. user = self.env.user
  238. res = []
  239. model = 'mail.wall.widgets.widget'
  240. domain = [('group_ids', 'in', user.groups_id.ids), ('active', '=', True)]
  241. for widget in self.env[model].search(domain, order='sequence'):
  242. if widget.cache:
  243. # TODO
  244. continue
  245. res.append({
  246. 'model': model,
  247. 'id': widget.id,
  248. 'currency': user.company_id.currency_id.id,
  249. 'data': widget.get_data(user)[0],
  250. })
  251. return res
  252. # def get_challenge_suggestions(self, cr, uid, context=None):
  253. # """Return the list of challenges suggested to the user"""
  254. # challenge_info = []
  255. # challenge_obj = self.pool.get('mail_wall_widgets.challenge')
  256. # challenge_ids = challenge_obj.search(cr, uid, [('invited_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
  257. # for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
  258. # values = {
  259. # 'id': challenge.id,
  260. # 'name': challenge.name,
  261. # 'description': challenge.description,
  262. # }
  263. # challenge_info.append(values)
  264. # return challenge_info