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.

339 lines
13 KiB

10 years ago
10 years ago
  1. # -*- coding: utf-8 -*-
  2. '''Extend res.partner model'''
  3. ##############################################################################
  4. #
  5. # OpenERP, Open Source Management Solution
  6. # This module copyright (C) 2013 Therp BV (<http://therp.nl>).
  7. #
  8. # This program is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU Affero General Public License as
  10. # published by the Free Software Foundation, either version 3 of the
  11. # License, or (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU Affero General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Affero General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. #
  21. ##############################################################################
  22. import time
  23. from openerp import osv, models, fields, exceptions, api
  24. from openerp.osv.expression import is_leaf, AND, OR, FALSE_LEAF
  25. from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
  26. from openerp.tools.translate import _
  27. PADDING = 10
  28. def get_partner_type(partner):
  29. """Get partner type for relation.
  30. :param partner: a res.partner either a company or not
  31. :return: 'c' for company or 'p' for person
  32. :rtype: str
  33. """
  34. return 'c' if partner.is_company else 'p'
  35. class ResPartner(models.Model):
  36. _inherit = 'res.partner'
  37. relation_count = fields.Integer(
  38. 'Relation Count',
  39. compute="_count_relations"
  40. )
  41. @api.one
  42. @api.depends("relation_ids")
  43. def _count_relations(self):
  44. """Count the number of relations this partner has for Smart Button
  45. Don't count inactive relations.
  46. """
  47. self.relation_count = len([r for r in self.relation_ids if r.active])
  48. def _get_relation_ids_select(self, cr, uid, ids, field_name, arg,
  49. context=None):
  50. '''return the partners' relations as tuple
  51. (id, left_partner_id, right_partner_id)'''
  52. cr.execute(
  53. '''select id, left_partner_id, right_partner_id
  54. from res_partner_relation
  55. where (left_partner_id in %s or right_partner_id in %s)''' +
  56. ' order by ' + self.pool['res.partner.relation']._order,
  57. (tuple(ids), tuple(ids))
  58. )
  59. return cr.fetchall()
  60. def _get_relation_ids(
  61. self, cr, uid, ids, field_name, arg, context=None):
  62. '''getter for relation_ids'''
  63. if context is None:
  64. context = {}
  65. result = dict([(i, []) for i in ids])
  66. # TODO: do a permission test on returned ids
  67. for row in self._get_relation_ids_select(
  68. cr, uid, ids, field_name, arg, context=context):
  69. if row[1] in result:
  70. result[row[1]].append(row[0])
  71. if row[2] in result:
  72. result[row[2]].append(row[0])
  73. return result
  74. def _set_relation_ids(
  75. self, cr, uid, ids, dummy_name, field_value, dummy_arg,
  76. context=None):
  77. '''setter for relation_ids'''
  78. if context is None:
  79. context = {}
  80. relation_obj = self.pool.get('res.partner.relation')
  81. context2 = self.with_partner_relations_context(
  82. cr, uid, ids, context=context).env.context
  83. for value in field_value:
  84. if value[0] == 0:
  85. relation_obj.create(cr, uid, value[2], context=context2)
  86. if value[0] == 1:
  87. # if we write partner_id_display, we also need to pass
  88. # type_selection_id in order to have this write end up on
  89. # the correct field
  90. if 'partner_id_display' in value[2] and 'type_selection_id'\
  91. not in value[2]:
  92. relation_data = relation_obj.read(
  93. cr, uid, [value[1]], ['type_selection_id'],
  94. context=context)[0]
  95. value[2]['type_selection_id'] =\
  96. relation_data['type_selection_id']
  97. relation_obj.write(
  98. cr, uid, value[1], value[2], context=context2)
  99. if value[0] == 2:
  100. relation_obj.unlink(cr, uid, value[1], context=context2)
  101. def _search_relation_id(
  102. self, cr, uid, dummy_obj, name, args, context=None):
  103. result = []
  104. for arg in args:
  105. if isinstance(arg, tuple) and arg[0] == name:
  106. if arg[1] not in ['=', '!=', 'like', 'not like', 'ilike',
  107. 'not ilike', 'in', 'not in']:
  108. raise exceptions.ValidationError(
  109. _('Unsupported search operand "%s"') % arg[1])
  110. relation_type_selection_ids = []
  111. relation_type_selection = self\
  112. .pool['res.partner.relation.type.selection']
  113. if arg[1] == '=' and isinstance(arg[2], (long, int)):
  114. relation_type_selection_ids.append(arg[2])
  115. elif arg[1] == '!=' and isinstance(arg[2], (long, int)):
  116. type_id, is_inverse = (
  117. relation_type_selection.browse(cr, uid, arg[2],
  118. context=context)
  119. .get_type_from_selection_id()
  120. )
  121. result = OR([
  122. result,
  123. [
  124. ('relation_all_ids.type_id', '!=', type_id),
  125. ]
  126. ])
  127. continue
  128. else:
  129. relation_type_selection_ids = relation_type_selection\
  130. .search(
  131. cr, uid,
  132. [
  133. ('type_id.name', arg[1], arg[2]),
  134. ('record_type', '=', 'a'),
  135. ],
  136. context=context)
  137. relation_type_selection_ids.extend(
  138. relation_type_selection.search(
  139. cr, uid,
  140. [
  141. ('type_id.name_inverse', arg[1], arg[2]),
  142. ('record_type', '=', 'b'),
  143. ],
  144. context=context))
  145. if not relation_type_selection_ids:
  146. result = AND([result, [FALSE_LEAF]])
  147. for relation_type_selection_id in relation_type_selection_ids:
  148. type_id, is_inverse = (
  149. relation_type_selection.browse(
  150. cr, uid, relation_type_selection_id,
  151. context=context
  152. ).get_type_from_selection_id()
  153. )
  154. result = OR([
  155. result,
  156. [
  157. '&',
  158. ('relation_all_ids.type_id', '=', type_id),
  159. ('relation_all_ids.record_type', '=',
  160. 'b' if is_inverse else 'a')
  161. ],
  162. ])
  163. return result
  164. def _search_relation_date(self, cr, uid, obj, name, args, context=None):
  165. result = []
  166. for arg in args:
  167. if isinstance(arg, tuple) and arg[0] == name:
  168. # TODO: handle {<,>}{,=}
  169. if arg[1] != '=':
  170. continue
  171. result.extend([
  172. '&',
  173. '|',
  174. ('relation_all_ids.date_start', '=', False),
  175. ('relation_all_ids.date_start', '<=', arg[2]),
  176. '|',
  177. ('relation_all_ids.date_end', '=', False),
  178. ('relation_all_ids.date_end', '>=', arg[2]),
  179. ])
  180. return result
  181. def _search_related_partner_id(
  182. self, cr, uid, dummy_obj, name, args, context=None):
  183. result = []
  184. for arg in args:
  185. if isinstance(arg, tuple) and arg[0] == name:
  186. result.append(
  187. (
  188. 'relation_all_ids.other_partner_id',
  189. arg[1],
  190. arg[2],
  191. ))
  192. return result
  193. def _search_related_partner_category_id(
  194. self, cr, uid, dummy_obj, name, args, context=None):
  195. result = []
  196. for arg in args:
  197. if isinstance(arg, tuple) and arg[0] == name:
  198. result.append(
  199. (
  200. 'relation_all_ids.other_partner_id.category_id',
  201. arg[1],
  202. arg[2],
  203. ))
  204. return result
  205. _columns = {
  206. 'relation_ids': osv.fields.function(
  207. lambda self, *args, **kwargs: self._get_relation_ids(
  208. *args, **kwargs),
  209. fnct_inv=_set_relation_ids,
  210. type='one2many', obj='res.partner.relation',
  211. string='Relations',
  212. selectable=False,
  213. ),
  214. 'relation_all_ids': osv.fields.one2many(
  215. 'res.partner.relation.all', 'this_partner_id',
  216. string='All relations with current partner',
  217. auto_join=True,
  218. selectable=False,
  219. ),
  220. 'search_relation_id': osv.fields.function(
  221. lambda self, cr, uid, ids, *args: dict([
  222. (i, False) for i in ids]),
  223. fnct_search=_search_relation_id,
  224. string='Has relation of type',
  225. type='many2one', obj='res.partner.relation.type.selection'
  226. ),
  227. 'search_relation_partner_id': osv.fields.function(
  228. lambda self, cr, uid, ids, *args: dict([
  229. (i, False) for i in ids]),
  230. fnct_search=_search_related_partner_id,
  231. string='Has relation with',
  232. type='many2one', obj='res.partner'
  233. ),
  234. 'search_relation_date': osv.fields.function(
  235. lambda self, cr, uid, ids, *args: dict([
  236. (i, False) for i in ids]),
  237. fnct_search=_search_relation_date,
  238. string='Relation valid', type='date'
  239. ),
  240. 'search_relation_partner_category_id': osv.fields.function(
  241. lambda self, cr, uid, ids, *args: dict([
  242. (i, False) for i in ids]),
  243. fnct_search=_search_related_partner_category_id,
  244. string='Has relation with a partner in category',
  245. type='many2one', obj='res.partner.category'
  246. ),
  247. }
  248. def copy_data(self, cr, uid, id, default=None, context=None):
  249. if default is None:
  250. default = {}
  251. default.setdefault('relation_ids', [])
  252. default.setdefault('relation_all_ids', [])
  253. return super(ResPartner, self).copy_data(cr, uid, id, default=default,
  254. context=context)
  255. def search(self, cr, uid, args, offset=0, limit=None, order=None,
  256. context=None, count=False):
  257. if context is None:
  258. context = {}
  259. # inject searching for current relation date if we search for relation
  260. # properties and no explicit date was given
  261. date_args = []
  262. for arg in args:
  263. if is_leaf(arg) and arg[0].startswith('search_relation'):
  264. if arg[0] == 'search_relation_date':
  265. date_args = []
  266. break
  267. if not date_args:
  268. date_args = [
  269. ('search_relation_date', '=', time.strftime(
  270. DEFAULT_SERVER_DATE_FORMAT))]
  271. # because of auto_join, we have to do the active test by hand
  272. active_args = []
  273. if context.get('active_test', True):
  274. for arg in args:
  275. if is_leaf(arg) and\
  276. arg[0].startswith('search_relation'):
  277. active_args = [('relation_all_ids.active', '=', True)]
  278. break
  279. return super(ResPartner, self).search(
  280. cr, uid, args + date_args + active_args, offset=offset,
  281. limit=limit, order=order, context=context, count=count)
  282. @api.v7
  283. def read(self, cr, user, ids, fields=None, context=None,
  284. load='_classic_read'):
  285. return super(ResPartner, self).read(
  286. cr, user, ids, fields=fields, context=context, load=load)
  287. @api.v8
  288. def read(self, fields=None, load='_classic_read'):
  289. return super(ResPartner, self.with_partner_relations_context())\
  290. .read(fields=fields, load=load)
  291. @api.multi
  292. def write(self, vals):
  293. return super(ResPartner, self.with_partner_relations_context())\
  294. .write(vals)
  295. @api.multi
  296. def with_partner_relations_context(self):
  297. context = dict(self.env.context)
  298. if context.get('active_model', self._name) == self._name:
  299. existing = self.exists()
  300. context.setdefault(
  301. 'active_id', existing.ids[0] if existing.ids else None)
  302. context.setdefault('active_ids', existing.ids)
  303. context.setdefault('active_model', self._name)
  304. return self.with_context(context)