Merge lp:~camptocamp/openerp-crm/7.0-crm_claim_merge-gbr into lp:~crm-core-editors/openerp-crm/7.0

Proposed by Guewen Baconnier @ Camptocamp on 2014-04-29
Status: Rejected
Rejected by: Guewen Baconnier @ Camptocamp on 2014-08-27
Proposed branch: lp:~camptocamp/openerp-crm/7.0-crm_claim_merge-gbr
Merge into: lp:~crm-core-editors/openerp-crm/7.0
Diff against target: 835 lines (+787/-0)
9 files modified
crm_claim_merge/__init__.py (+4/-0)
crm_claim_merge/__openerp__.py (+45/-0)
crm_claim_merge/crm_claim.py (+370/-0)
crm_claim_merge/i18n/crm_claim_merge.pot (+69/-0)
crm_claim_merge/i18n/fr.po (+69/-0)
crm_claim_merge/test/crm_claim_merge.yml (+103/-0)
crm_claim_merge/wizard/__init__.py (+3/-0)
crm_claim_merge/wizard/crm_claim_merge.py (+83/-0)
crm_claim_merge/wizard/crm_claim_merge_view.xml (+41/-0)
To merge this branch: bzr merge lp:~camptocamp/openerp-crm/7.0-crm_claim_merge-gbr
Reviewer Review Type Date Requested Status
OpenERP CRM Core Editors 2014-04-29 Pending
Review via email: mp+217628@code.launchpad.net

Commit message

Add crm_claim_merge, allows to merge claims.

To post a comment you must log in.
24. By Guewen Baconnier @ Camptocamp on 2014-04-29

useless copy

25. By Guewen Baconnier @ Camptocamp on 2014-04-30

add translation pot and fr translation

Unmerged revisions

25. By Guewen Baconnier @ Camptocamp on 2014-04-30

add translation pot and fr translation

24. By Guewen Baconnier @ Camptocamp on 2014-04-29

useless copy

23. By Guewen Baconnier @ Camptocamp on 2014-04-29

use the usual tree view for displaying claims in the wizard

22. By Guewen Baconnier @ Camptocamp on 2014-04-29

avoid collisions in attachment names

21. By Guewen Baconnier @ Camptocamp on 2014-04-29

use name_get() in subject of summary so if it has been customized (eg. crm_claim_rma with a sequence), it will be displayed correctly

20. By Guewen Baconnier @ Camptocamp on 2014-04-29

use name_get() when writing the subject of the merged messages

19. By Guewen Baconnier @ Camptocamp on 2014-04-29

completed tests with more types of fields (added date, selection, reference, m2o, char)

18. By Guewen Baconnier @ Camptocamp on 2014-04-29

fix display of one2many fields in the summary message after the merge

17. By Guewen Baconnier @ Camptocamp on 2014-04-29

merge FK pointing to crm_claim (o2m and m2m)

16. By Guewen Baconnier @ Camptocamp on 2014-04-29

introspect the list of fields to merge rather than hardcoding them

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'crm_claim_merge'
2=== added file 'crm_claim_merge/__init__.py'
3--- crm_claim_merge/__init__.py 1970-01-01 00:00:00 +0000
4+++ crm_claim_merge/__init__.py 2014-04-30 14:21:25 +0000
5@@ -0,0 +1,4 @@
6+# -*- coding: utf-8 -*-
7+
8+from . import crm_claim
9+from . import wizard
10
11=== added file 'crm_claim_merge/__openerp__.py'
12--- crm_claim_merge/__openerp__.py 1970-01-01 00:00:00 +0000
13+++ crm_claim_merge/__openerp__.py 2014-04-30 14:21:25 +0000
14@@ -0,0 +1,45 @@
15+# -*- coding: utf-8 -*-
16+##############################################################################
17+#
18+# Author: Guewen Baconnier
19+# Copyright 2014 Camptocamp SA
20+#
21+# This program is free software: you can redistribute it and/or modify
22+# it under the terms of the GNU Affero General Public License as
23+# published by the Free Software Foundation, either version 3 of the
24+# License, or (at your option) any later version.
25+#
26+# This program is distributed in the hope that it will be useful,
27+# but WITHOUT ANY WARRANTY; without even the implied warranty of
28+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29+# GNU Affero General Public License for more details.
30+#
31+# You should have received a copy of the GNU Affero General Public License
32+# along with this program. If not, see <http://www.gnu.org/licenses/>.
33+#
34+##############################################################################
35+
36+{'name': 'Claims Merge',
37+ 'version': '1.0',
38+ 'author': 'Camptocamp',
39+ 'maintainer': 'Camptocamp',
40+ 'license': 'AGPL-3',
41+ 'category': 'Customer Relationship Management',
42+ 'depends': ['crm_claim',
43+ ],
44+ 'description': """
45+Claims Merge
46+============
47+
48+Select claims and use the 'Merge' option menu to merge them.
49+The messages will be grouped in one thread.
50+
51+""",
52+ 'website': 'http://www.camptocamp.com',
53+ 'data': ['wizard/crm_claim_merge_view.xml',
54+ ],
55+ 'test': ['test/crm_claim_merge.yml',
56+ ],
57+ 'installable': True,
58+ 'auto_install': False,
59+}
60
61=== added file 'crm_claim_merge/crm_claim.py'
62--- crm_claim_merge/crm_claim.py 1970-01-01 00:00:00 +0000
63+++ crm_claim_merge/crm_claim.py 2014-04-30 14:21:25 +0000
64@@ -0,0 +1,370 @@
65+# -*- coding: utf-8 -*-
66+##############################################################################
67+#
68+# Author: Guewen Baconnier
69+# Copyright 2014 Camptocamp SA
70+# Merge code freely adapted to claims from merge of crm leads
71+# and partners by OpenERP
72+# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
73+#
74+# This program is free software: you can redistribute it and/or modify
75+# it under the terms of the GNU Affero General Public License as
76+# published by the Free Software Foundation, either version 3 of the
77+# License, or (at your option) any later version.
78+#
79+# This program is distributed in the hope that it will be useful,
80+# but WITHOUT ANY WARRANTY; without even the implied warranty of
81+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
82+# GNU Affero General Public License for more details.
83+#
84+# You should have received a copy of the GNU Affero General Public License
85+# along with this program. If not, see <http://www.gnu.org/licenses/>.
86+#
87+##############################################################################
88+
89+from operator import attrgetter
90+from itertools import chain
91+
92+from openerp import SUPERUSER_ID
93+from openerp.osv import orm
94+from openerp.tools.translate import _
95+
96+CRM_CLAIM_FIELD_BLACKLIST = [
97+ 'message_ids',
98+ 'message_follower_ids',
99+]
100+
101+
102+class crm_claim(orm.Model):
103+ _inherit = 'crm.claim'
104+
105+ def _merge_get_default_main(self, cr, uid, claims, context=None):
106+ return sorted(claims, key=attrgetter('date'))[0]
107+
108+ def _merge_sort(self, cr, uid, claims, context=None):
109+ """ Sort the tailing claims.
110+
111+ When claims have concurrent values, the (true-ish) value from the
112+ first claim is used.
113+ """
114+ return sorted(claims, key=attrgetter('date'))
115+
116+ def _merge_check(self, cr, uid, claims, context=None):
117+ if len(claims) <= 1:
118+ raise orm.except_orm(
119+ _('Warning'),
120+ _('Please select more than one claim from the list view.'))
121+
122+ partner = next((claim.partner_id for claim in claims), None)
123+ if partner:
124+ if any(claim.partner_id != partner for claim in claims
125+ if claim.partner_id):
126+ raise orm.except_orm(
127+ _('Error'),
128+ _('Cannot merge claims of different partners.'))
129+
130+ def _merge_fields(self, cr, uid, context=None):
131+ fields = self._all_columns
132+ fields = (name for name, info in fields.iteritems()
133+ if not info.column.readonly)
134+ fields = (name for name in fields if
135+ name not in CRM_CLAIM_FIELD_BLACKLIST)
136+ return list(fields)
137+
138+ def _get_fk_on(self, cr, table):
139+ """ Get all FK pointing to a table """
140+ q = """ SELECT cl1.relname as table,
141+ att1.attname as column
142+ FROM pg_constraint as con, pg_class as cl1, pg_class as cl2,
143+ pg_attribute as att1, pg_attribute as att2
144+ WHERE con.conrelid = cl1.oid
145+ AND con.confrelid = cl2.oid
146+ AND array_lower(con.conkey, 1) = 1
147+ AND con.conkey[1] = att1.attnum
148+ AND att1.attrelid = cl1.oid
149+ AND cl2.relname = %s
150+ AND att2.attname = 'id'
151+ AND array_lower(con.confkey, 1) = 1
152+ AND con.confkey[1] = att2.attnum
153+ AND att2.attrelid = cl2.oid
154+ AND con.contype = 'f'
155+ """
156+ return cr.execute(q, (table,))
157+
158+ def _merge_update_foreign_keys(self, cr, uid, merge_in,
159+ claims, context=None):
160+ claim_ids = [claim.id for claim in claims]
161+
162+ # find the many2one relation to a claim
163+ self._get_fk_on(cr, 'crm_claim')
164+
165+ for table, column in cr.fetchall():
166+ if 'crm_claim_merge' in table:
167+ # ignore the wizard tables (TransientModel + relation
168+ # table)
169+ continue
170+ query = ("SELECT column_name FROM information_schema.columns"
171+ " WHERE table_name = %s")
172+ cr.execute(query, (table, ))
173+ columns = []
174+ for data in cr.fetchall():
175+ if data[0] != column:
176+ columns.append(data[0])
177+
178+ query_dic = {
179+ 'table': table,
180+ 'column': column,
181+ 'value': columns[0],
182+ }
183+ if len(columns) <= 1:
184+ # update of m2m
185+ query = """
186+ UPDATE "%(table)s" as ___tu
187+ SET %(column)s = %%s
188+ WHERE
189+ %(column)s = %%s AND
190+ NOT EXISTS (
191+ SELECT 1
192+ FROM "%(table)s" as ___tw
193+ WHERE
194+ %(column)s = %%s AND
195+ ___tu.%(value)s = ___tw.%(value)s
196+ )""" % query_dic
197+ for claim_id in claim_ids:
198+ cr.execute(query, (merge_in.id, claim_id,
199+ merge_in.id))
200+ else:
201+ query = ('UPDATE "%(table)s" SET %(column)s = %%s WHERE '
202+ '%(column)s IN %%s') % query_dic
203+ cr.execute(query, (merge_in.id, tuple(claim_ids)))
204+
205+ def _merge_data(self, cr, uid, merge_in, claims, fields, context=None):
206+ """
207+ Prepare claims data into a dictionary for merging. Different types
208+ of fields are processed in different ways:
209+ - text: all the values are concatenated
210+ - m2m and o2m: those fields aren't processed
211+ - m2o: the first not null value prevails (the other are dropped)
212+ - any other type of field: same as m2o
213+
214+ :param merge_in: other claims will be merged in this one
215+ :param claims: list of claims to merge
216+ :param fields: list of leads' fields to process
217+ :return data: contains the merged values
218+ """
219+ claims = [merge_in] + claims
220+
221+ def _get_first_not_falsish(attr):
222+ for claim in claims:
223+ value = getattr(claim, attr, None)
224+ if value:
225+ return value
226+ return False
227+
228+ def _get_first_reference(attr):
229+ rel = _get_first_not_falsish(attr)
230+ return '%s,%s' % (rel._model._name, rel.id) if rel else False
231+
232+ def _get_first_m2o(attr):
233+ rel = _get_first_not_falsish(attr)
234+ return rel.id if rel else False
235+
236+ def _concat_text(attr):
237+ return '\n\n'.join([getattr(claim, attr) or '' for claim in claims
238+ if hasattr(claim, attr)])
239+
240+ # Process the fields' values
241+ data = {}
242+ for field_name in fields:
243+ field_info = self._all_columns.get(field_name)
244+ if field_info is None:
245+ continue
246+ field = field_info.column
247+ field_type = field._type # noqa
248+ if field_type in ('many2many', 'one2many'):
249+ continue
250+ elif field_type == 'many2one':
251+ data[field_name] = _get_first_m2o(field_name)
252+ elif field_type == 'text':
253+ data[field_name] = _concat_text(field_name)
254+ elif field_type == 'reference':
255+ data[field_name] = _get_first_reference(field_name)
256+ else:
257+ data[field_name] = _get_first_not_falsish(field_name)
258+
259+ # Check if the stage is in the stages of the sales team. If not,
260+ # assign the stage with the lowest sequence
261+ if data.get('section_id'):
262+ stage_obj = self.pool['crm.case.stage']
263+ section_stage_ids = stage_obj.search(
264+ cr, uid,
265+ [('section_ids', 'in', data['section_id'])],
266+ order='sequence',
267+ context=context)
268+ if data.get('stage_id') not in section_stage_ids:
269+ data['stage_id'] = (section_stage_ids[0] if
270+ section_stage_ids else False)
271+ return data
272+
273+ def _merge_claim_history(self, cr, uid, merge_in, claims, context=None):
274+ merge_in_id = merge_in.id
275+ for claim in claims:
276+ history_ids = set()
277+ for history in claim.message_ids:
278+ history_ids.add(history.id)
279+ message = self.pool['mail.message']
280+ message.write(cr, uid,
281+ list(history_ids),
282+ {'res_id': merge_in_id,
283+ 'subject': _("From %s") % claim.name_get()[0][1],
284+ },
285+ context=context)
286+
287+ def _merge_claim_attachments(self, cr, uid, merge_in, claims, context=None):
288+ attach_obj = self.pool['ir.attachment']
289+
290+ # return attachments of claims
291+ def _get_attachments(claim_id):
292+ attachment_ids = attach_obj.search(
293+ cr, uid,
294+ [('res_model', '=', self._name),
295+ ('res_id', '=', claim_id)],
296+ context=context)
297+ return attach_obj.browse(cr, uid, attachment_ids, context=context)
298+
299+ first_attachments = _get_attachments(merge_in.id)
300+ merge_in_id = merge_in.id
301+
302+ # Counter of all attachments to move.
303+ # Used to make sure the name is different for all attachments
304+ existing_names = [att.name for att in first_attachments]
305+ for claim in claims:
306+ attachments = _get_attachments(claim.id)
307+ for attachment in attachments:
308+ values = {'res_id': merge_in_id}
309+ name = attachment.name
310+ count = 1
311+ while name in existing_names:
312+ name = "%s (%s)" % (attachment.name, count)
313+ count += 1
314+ values['name'] = name
315+ attachment.write(values)
316+ existing_names.append(name)
317+
318+ def _merge_mail_body(self, cr, uid, claim, fields, title=False, context=None):
319+ body = []
320+ if title:
321+ body.append("%s\n" % title)
322+
323+ for field_name in fields:
324+ field_info = self._all_columns.get(field_name)
325+ if field_info is None:
326+ continue
327+ field = field_info.column
328+ value = ''
329+
330+ field_type = field._type # noqa
331+
332+ if field_type == 'selection':
333+ if hasattr(field.selection, '__call__'):
334+ key = field.selection(self, cr, uid, context=context)
335+ else:
336+ key = field.selection
337+ value = dict(key).get(claim[field_name], claim[field_name])
338+ elif field_type in ('many2one', 'reference'):
339+ if claim[field_name]:
340+ value = claim[field_name].name_get()[0][1]
341+ elif field_type in ('many2many', 'one2many'):
342+ if claim[field_name]:
343+ for val in claim[field_name]:
344+ field_value = val.name_get()[0][1]
345+ value += field_value + ","
346+ else:
347+ value = claim[field_name]
348+
349+ body.append("%s: %s" % (field.string, value or ''))
350+ return "<br/>".join(body + ['<br/>'])
351+
352+ def _merge_notify(self, cr, uid, merge_in, claims, context=None):
353+ """ Create a message gathering merged claims information. """
354+ details = []
355+ subject = [_('Merged claims')]
356+ for claim in chain([merge_in] + claims):
357+ name = claim.name_get()[0][1]
358+ subject.append(name)
359+ title = "%s: %s" % (_('Merged claim'), name)
360+ fields = list(self._merge_fields(cr, uid, context=context))
361+ details.append(self._merge_mail_body(cr, uid, claim, fields,
362+ title=title, context=context))
363+
364+ # Chatter message's subject
365+ subject = subject[0] + ": " + ", ".join(subject[1:])
366+ details = "\n\n".join(details)
367+ return self.message_post(cr, uid, [merge_in.id],
368+ body=details, subject=subject,
369+ context=context)
370+
371+ def _merge_followers(self, cr, uid, merge_in, claims, context=None):
372+ """ Subscribe the same followers on the final claim. """
373+ follower_ids = [fol.id for fol in merge_in.message_follower_ids]
374+
375+ fol_obj = self.pool.get('mail.followers')
376+ fol_ids = fol_obj.search(
377+ cr, SUPERUSER_ID,
378+ [('res_model', '=', self._name),
379+ ('res_id', 'in', [claim.id for claim in claims])],
380+ context=context)
381+
382+ for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context):
383+ if fol.res_id in follower_ids:
384+ continue
385+ subtype_ids = [st.id for st in fol.subtype_ids]
386+ self.message_subscribe(cr, SUPERUSER_ID, [merge_in.id],
387+ [fol.partner_id.id],
388+ subtype_ids=subtype_ids,
389+ context=context)
390+
391+ def merge(self, cr, uid, ids, merge_in_id=None, context=None):
392+ """ Merge claims together.
393+
394+ :param merge_in_ids: the other claims will be merged into this one
395+ if None, the oldest claim will be selected.
396+ """
397+ claims = self.browse(cr, uid, ids, context=context)
398+ self._merge_check(cr, uid, claims, context=context)
399+ if merge_in_id is None:
400+ merge_in = self._merge_get_default_main(cr, uid, claims,
401+ context=context)
402+ else:
403+ for claim in claims:
404+ if claim.id == merge_in_id:
405+ merge_in = claim
406+ break
407+ claims.remove(merge_in) # keep the tail
408+ claims = self._merge_sort(cr, uid, claims, context=context)
409+
410+ fields = list(self._merge_fields(cr, uid, context=None))
411+ data = self._merge_data(cr, uid, merge_in, claims,
412+ fields, context=context)
413+
414+ self._merge_claim_history(cr, uid, merge_in, claims, context=context)
415+ self._merge_claim_attachments(cr, uid, merge_in, claims,
416+ context=context)
417+
418+ self._merge_notify(cr, uid, merge_in, claims, context=context)
419+ self._merge_followers(cr, uid, merge_in, claims, context=context)
420+
421+ self._merge_update_foreign_keys(cr, uid, merge_in,
422+ claims, context=context)
423+ # Write merged data into first claim
424+ self.write(cr, uid, [merge_in.id], data, context=context)
425+
426+ # Delete tail claims
427+ # We use the SUPERUSER to avoid access rights issues because as
428+ # the user had the rights to see the records it should be safe
429+ # to do so
430+ self.unlink(cr, SUPERUSER_ID,
431+ [claim.id for claim in claims],
432+ context=context)
433+
434+ return merge_in.id
435
436=== added directory 'crm_claim_merge/i18n'
437=== added file 'crm_claim_merge/i18n/crm_claim_merge.pot'
438--- crm_claim_merge/i18n/crm_claim_merge.pot 1970-01-01 00:00:00 +0000
439+++ crm_claim_merge/i18n/crm_claim_merge.pot 2014-04-30 14:21:25 +0000
440@@ -0,0 +1,69 @@
441+# Translation of OpenERP Server.
442+# This file contains the translation of the following modules:
443+# * crm_claim_merge
444+#
445+msgid ""
446+msgstr ""
447+"Project-Id-Version: OpenERP Server 7.0\n"
448+"Report-Msgid-Bugs-To: \n"
449+"POT-Creation-Date: 2014-04-30 14:15+0000\n"
450+"PO-Revision-Date: 2014-04-30 14:15+0000\n"
451+"Last-Translator: <>\n"
452+"Language-Team: \n"
453+"MIME-Version: 1.0\n"
454+"Content-Type: text/plain; charset=UTF-8\n"
455+"Content-Transfer-Encoding: \n"
456+"Plural-Forms: \n"
457+
458+#. module: crm_claim_merge
459+#: view:crm.claim.merge:0
460+msgid "Cancel"
461+msgstr ""
462+
463+#. module: crm_claim_merge
464+#: code:_description:0
465+#: model:ir.model,name:crm_claim_merge.model_crm_claim
466+#, python-format
467+msgid "Claim"
468+msgstr ""
469+
470+#. module: crm_claim_merge
471+#: field:crm.claim.merge,claim_ids:0
472+msgid "Claims"
473+msgstr ""
474+
475+#. module: crm_claim_merge
476+#: view:crm.claim.merge:0
477+msgid "Merge"
478+msgstr ""
479+
480+#. module: crm_claim_merge
481+#: code:_description:0
482+#: view:crm.claim.merge:0
483+#: model:ir.actions.act_window,name:crm_claim_merge.action_crm_claim_merge
484+#: model:ir.actions.act_window,name:crm_claim_merge.action_merge_claim
485+#: model:ir.model,name:crm_claim_merge.model_crm_claim_merge
486+#, python-format
487+msgid "Merge Claims"
488+msgstr ""
489+
490+#. module: crm_claim_merge
491+#: field:crm.claim.merge,merge_in_id:0
492+msgid "Merge in"
493+msgstr ""
494+
495+#. module: crm_claim_merge
496+#: view:crm.claim.merge:0
497+msgid "Select Claims"
498+msgstr ""
499+
500+#. module: crm_claim_merge
501+#: help:crm.claim.merge,merge_in_id:0
502+msgid "The other claims will be merged into this one."
503+msgstr ""
504+
505+#. module: crm_claim_merge
506+#: view:crm.claim.merge:0
507+msgid "or"
508+msgstr ""
509+
510
511=== added file 'crm_claim_merge/i18n/fr.po'
512--- crm_claim_merge/i18n/fr.po 1970-01-01 00:00:00 +0000
513+++ crm_claim_merge/i18n/fr.po 2014-04-30 14:21:25 +0000
514@@ -0,0 +1,69 @@
515+# Translation of OpenERP Server.
516+# This file contains the translation of the following modules:
517+# * crm_claim_merge
518+#
519+msgid ""
520+msgstr ""
521+"Project-Id-Version: OpenERP Server 7.0\n"
522+"Report-Msgid-Bugs-To: \n"
523+"POT-Creation-Date: 2014-04-30 14:16+0000\n"
524+"PO-Revision-Date: 2014-04-30 14:16+0000\n"
525+"Last-Translator: <>\n"
526+"Language-Team: \n"
527+"MIME-Version: 1.0\n"
528+"Content-Type: text/plain; charset=UTF-8\n"
529+"Content-Transfer-Encoding: \n"
530+"Plural-Forms: \n"
531+
532+#. module: crm_claim_merge
533+#: view:crm.claim.merge:0
534+msgid "Cancel"
535+msgstr "Annuler"
536+
537+#. module: crm_claim_merge
538+#: code:_description:0
539+#: model:ir.model,name:crm_claim_merge.model_crm_claim
540+#, python-format
541+msgid "Claim"
542+msgstr "Réclamation"
543+
544+#. module: crm_claim_merge
545+#: field:crm.claim.merge,claim_ids:0
546+msgid "Claims"
547+msgstr "Réclamations"
548+
549+#. module: crm_claim_merge
550+#: view:crm.claim.merge:0
551+msgid "Merge"
552+msgstr "Fusion"
553+
554+#. module: crm_claim_merge
555+#: code:_description:0
556+#: view:crm.claim.merge:0
557+#: model:ir.actions.act_window,name:crm_claim_merge.action_crm_claim_merge
558+#: model:ir.actions.act_window,name:crm_claim_merge.action_merge_claim
559+#: model:ir.model,name:crm_claim_merge.model_crm_claim_merge
560+#, python-format
561+msgid "Merge Claims"
562+msgstr "Fusionner des réclamations"
563+
564+#. module: crm_claim_merge
565+#: field:crm.claim.merge,merge_in_id:0
566+msgid "Merge in"
567+msgstr "Fusionner dans"
568+
569+#. module: crm_claim_merge
570+#: view:crm.claim.merge:0
571+msgid "Select Claims"
572+msgstr "Choisir les réclamations"
573+
574+#. module: crm_claim_merge
575+#: help:crm.claim.merge,merge_in_id:0
576+msgid "The other claims will be merged into this one."
577+msgstr "Les autres réclamations seront fusionnées dans celle-ci."
578+
579+#. module: crm_claim_merge
580+#: view:crm.claim.merge:0
581+msgid "or"
582+msgstr "ou"
583+
584
585=== added directory 'crm_claim_merge/test'
586=== added file 'crm_claim_merge/test/crm_claim_merge.yml'
587--- crm_claim_merge/test/crm_claim_merge.yml 1970-01-01 00:00:00 +0000
588+++ crm_claim_merge/test/crm_claim_merge.yml 2014-04-30 14:21:25 +0000
589@@ -0,0 +1,103 @@
590+-
591+ Create claims to test their merge
592+-
593+ !record {model: crm.claim, id: test_crm_claim_01}:
594+ name: 'Test claim 1'
595+ date: !eval time.strftime('%Y-02-08 00:00:00')
596+ partner_id: base.res_partner_3
597+ stage_id: crm_claim.stage_claim1
598+ description: This is the description of the test claim 1.
599+ partner_phone: (373) 907-1009
600+ email_from: bill@example.com
601+ ref: !eval ('res.partner,%d' % ref('base.res_partner_5'))
602+ type_action: correction
603+-
604+ !record {model: crm.claim, id: test_crm_claim_02}:
605+ name: 'Test claim 2'
606+ date: !eval time.strftime('%Y-02-04 00:00:00')
607+ partner_id: base.res_partner_3
608+ stage_id: crm_claim.stage_claim5
609+ description: This is the description of the test claim 2.
610+ partner_phone: (373) 907-1009
611+ email_from: bill@example.com
612+-
613+ !record {model: crm.claim, id: test_crm_claim_03}:
614+ name: 'Test claim 3'
615+ date: !eval time.strftime('%Y-02-06 00:00:00')
616+ partner_id: base.res_partner_3
617+ stage_id: crm_claim.stage_claim3
618+ description: This is the description of the test claim 3.
619+ partner_phone: (373) 907-1010
620+ email_from: tom@example.com
621+ type_action: prevention
622+ section_id: crm.section_sales_department
623+-
624+ !python {model: crm.claim}: |
625+ claim_ids = [ref('test_crm_claim_01'),
626+ ref('test_crm_claim_02'),
627+ ref('test_crm_claim_03')]
628+ context.update({'active_model': 'crm.claim',
629+ 'active_ids': claim_ids,
630+ 'active_id': claim_ids[0]})
631+-
632+ I create a merge wizard and merge the claims together in the second claim
633+-
634+ !record {model: crm.claim.merge, id: merge_claim_wizard_01}:
635+-
636+ !python {model: crm.claim.merge}: |
637+ self.action_merge(cr, uid, [ref("merge_claim_wizard_01")], context=context)
638+-
639+ I check for the resulting merged claim (based on name and partner).
640+-
641+ !python {model: crm.claim}: |
642+ merge_id = self.search(cr, uid,
643+ [('name', '=', 'Test claim 2'),
644+ ('partner_id','=', ref("base.res_partner_3"))])
645+ assert merge_id, 'Failed to create the merged claim'
646+ claim = self.browse(cr, uid, merge_id[0])
647+ expected = ('This is the description of the test claim 2.\n\n'
648+ 'This is the description of the test claim 3.\n\n'
649+ 'This is the description of the test claim 1.')
650+ assert claim.description == expected, (
651+ 'Description mismatch: when merging claims with '
652+ 'different text values, these values should get '
653+ 'concatenated and separated with line returns')
654+ expected = 'bill@example.com'
655+ assert claim.email_from == expected, (
656+ 'Email mismatch, expected %s, got: %s' %
657+ (expected, claim.email_from))
658+ import time
659+ expected = time.strftime('%Y-02-04 00:00:00')
660+ assert claim.date == expected, (
661+ 'Date mismatch, expected %s, got: %s' %
662+ (expected, claim.date))
663+ expected = ref('crm_claim.stage_claim5')
664+ assert claim.stage_id.id == expected, (
665+ 'Stage mismatch, expected %s, got: %s' %
666+ (expected, claim.stage_id.id))
667+ expected = '(373) 907-1009'
668+ assert claim.partner_phone == expected, (
669+ 'Phone mismatch, expected %s, got: %s' %
670+ (expected, claim.partner_phone))
671+ expected = ref('base.res_partner_5')
672+ assert (claim.ref.id == expected
673+ and claim.ref._model._name == 'res.partner'), (
674+ 'Reference mismatch, expected a partner with id %s, got: %s' %
675+ (expected, claim.ref))
676+ expected = 'prevention'
677+ assert claim.type_action == expected, (
678+ 'Action Type mismatch, expected %s, got: %s' %
679+ (expected, claim.type_action))
680+ expected = ref('crm.section_sales_department')
681+ assert claim.section_id.id == expected, (
682+ 'Section mismatch, expected %s, got: %s' %
683+ (expected, claim.section_id.id))
684+-
685+ The other (tailing) claims shouldn't exist anymore.
686+-
687+ !python {model: crm.claim}: |
688+ tailing_claim = self.search(cr, uid, [('id', '=', ref('test_crm_claim_01'))])
689+ assert not tailing_claim, 'This tailing claim (id %s) should not exist anymore' % ref('test_crm_claim_01')
690+
691+ tailing_claim = self.search(cr, uid, [('id', '=', ref('test_crm_claim_03'))])
692+ assert not tailing_claim, 'This tailing claim (id %s) should not exist anymore' % ref('test_crm_claim_03')
693
694=== added directory 'crm_claim_merge/wizard'
695=== added file 'crm_claim_merge/wizard/__init__.py'
696--- crm_claim_merge/wizard/__init__.py 1970-01-01 00:00:00 +0000
697+++ crm_claim_merge/wizard/__init__.py 2014-04-30 14:21:25 +0000
698@@ -0,0 +1,3 @@
699+# -*- coding: utf-8 -*-
700+
701+from . import crm_claim_merge
702
703=== added file 'crm_claim_merge/wizard/crm_claim_merge.py'
704--- crm_claim_merge/wizard/crm_claim_merge.py 1970-01-01 00:00:00 +0000
705+++ crm_claim_merge/wizard/crm_claim_merge.py 2014-04-30 14:21:25 +0000
706@@ -0,0 +1,83 @@
707+# -*- coding: utf-8 -*-
708+##############################################################################
709+#
710+# Author: Guewen Baconnier
711+# Copyright 2014 Camptocamp SA
712+#
713+# This program is free software: you can redistribute it and/or modify
714+# it under the terms of the GNU Affero General Public License as
715+# published by the Free Software Foundation, either version 3 of the
716+# License, or (at your option) any later version.
717+#
718+# This program is distributed in the hope that it will be useful,
719+# but WITHOUT ANY WARRANTY; without even the implied warranty of
720+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
721+# GNU Affero General Public License for more details.
722+#
723+# You should have received a copy of the GNU Affero General Public License
724+# along with this program. If not, see <http://www.gnu.org/licenses/>.
725+#
726+##############################################################################
727+
728+from openerp.osv import orm, fields
729+from openerp.tools.translate import _
730+
731+
732+class crm_claim_merge(orm.TransientModel):
733+ """ Merge claims together. """
734+
735+ _name = 'crm.claim.merge'
736+ _description = 'Merge Claims'
737+ _columns = {
738+ 'claim_ids': fields.many2many('crm.claim',
739+ string='Claims'),
740+ 'merge_in_id': fields.many2one(
741+ 'crm.claim',
742+ string='Merge in',
743+ domain="[('id', 'in', claim_ids[0][2])]",
744+ help="The other claims will be merged "
745+ "into this one."),
746+ }
747+
748+ def redirect_new_claim(self, cr, uid, claim_id, context=None):
749+ models_data = self.pool.get('ir.model.data')
750+ __, form_view = models_data.get_object_reference(
751+ cr, uid, 'crm_claim', 'crm_case_claims_form_view')
752+ return {
753+ 'name': _('Claim'),
754+ 'view_type': 'form',
755+ 'view_mode': 'form',
756+ 'res_model': 'crm.claim',
757+ 'res_id': int(claim_id),
758+ 'view_id': form_view,
759+ 'type': 'ir.actions.act_window',
760+ }
761+
762+ def action_merge(self, cr, uid, ids, context=None):
763+ claim_obj = self.pool['crm.claim']
764+ if isinstance(ids, (tuple, list)):
765+ assert len(ids) == 1, "Expect 1 ID, got: %s" % ids
766+ ids = ids[0]
767+ wizard = self.browse(cr, uid, ids, context=context)
768+ merge_ids = [claim.id for claim in wizard.claim_ids]
769+ merged_id = claim_obj.merge(cr, uid, merge_ids,
770+ merge_in_id=wizard.merge_in_id.id,
771+ context=context)
772+ return self.redirect_new_claim(cr, uid, merged_id, context=context)
773+
774+ def default_get(self, cr, uid, fields, context=None):
775+ """
776+ Use active_ids from the context to fetch the claims to merge.
777+ """
778+ if context is None:
779+ context = {}
780+ res = super(crm_claim_merge, self).default_get(
781+ cr, uid, fields, context=context)
782+ if 'claim_ids' in fields:
783+ res['claim_ids'] = claim_ids = context.get('active_ids')
784+ if 'merge_in_id' in fields and claim_ids:
785+ claim_obj = self.pool['crm.claim']
786+ claims = claim_obj.browse(cr, uid, claim_ids, context=context)
787+ res['merge_in_id'] = claim_obj._merge_get_default_main(
788+ cr, uid, claims, context=context).id
789+ return res
790
791=== added file 'crm_claim_merge/wizard/crm_claim_merge_view.xml'
792--- crm_claim_merge/wizard/crm_claim_merge_view.xml 1970-01-01 00:00:00 +0000
793+++ crm_claim_merge/wizard/crm_claim_merge_view.xml 2014-04-30 14:21:25 +0000
794@@ -0,0 +1,41 @@
795+<?xml version="1.0"?>
796+<openerp>
797+ <data>
798+
799+ <!-- Merge Claims -->
800+ <record model="ir.ui.view" id="view_crm_claim_merge_form">
801+ <field name="name">crm.claim.merge.form</field>
802+ <field name="model">crm.claim.merge</field>
803+ <field name="arch" type="xml">
804+ <form string="Merge Claims" version="7.0">
805+ <group string="Select Claims">
806+ <field name="claim_ids"/>
807+ <field name="merge_in_id" />
808+ </group>
809+ <footer>
810+ <button name="action_merge" type="object" string="Merge" class="oe_highlight"/>
811+ or
812+ <button string="Cancel" class="oe_link" special="cancel"/>
813+ </footer>
814+ </form>
815+ </field>
816+ </record>
817+
818+ <!-- Merge leads/claim action -->
819+ <record model="ir.actions.act_window" id="action_crm_claim_merge">
820+ <field name="name">Merge Claims</field>
821+ <field name="res_model">crm.claim.merge</field>
822+ <field name="view_type">form</field>
823+ <field name="view_mode">form</field>
824+ <field name="view_id" ref="view_crm_claim_merge_form"/>
825+ <field name="target">new</field>
826+ </record>
827+
828+ <act_window id="action_merge_claim"
829+ multi="True"
830+ key2="client_action_multi" name="Merge Claims"
831+ res_model="crm.claim.merge" src_model="crm.claim"
832+ view_mode="form" target="new" view_type="form"/>
833+
834+ </data>
835+</openerp>

Subscribers

People subscribed via source and target branches