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.

273 lines
12 KiB

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