[MERGE]: Merged with trunk-addons.
[odoo/odoo.git] / addons / crm_profiling / crm_profiling.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import fields,osv
23 from osv import orm
24
25 from tools.translate import _
26
27 def _get_answers(cr, uid, ids):
28     """
29         @param cr: the current row, from the database cursor,
30         @param uid: the current user’s ID for security checks,
31         @param ids: List of crm profiling’s IDs """
32
33     query = """
34     select distinct(answer)
35     from profile_question_yes_rel
36     where profile IN %s"""
37
38     cr.execute(query, (tuple(ids),))
39     ans_yes = [x[0] for x in cr.fetchall()]
40
41     query = """
42     select distinct(answer)
43     from profile_question_no_rel
44     where profile IN %s"""
45
46     cr.execute(query, (tuple(ids),))
47     ans_no = [x[0] for x in cr.fetchall()]
48
49     return [ans_yes, ans_no]
50
51
52 def _get_parents(cr, uid, ids):
53     """
54         @param cr: the current row, from the database cursor,
55         @param uid: the current user’s ID for security checks,
56         @param ids: List of crm profiling’s IDs
57         @return: Get parents's Id """
58
59     ids_to_check = ids
60     cr.execute("""
61      select distinct(parent_id)
62      from crm_segmentation
63      where parent_id is not null
64      and id IN %s""",(tuple(ids),))
65
66     parent_ids = [x[0] for x in cr.fetchall()]
67
68     trigger = False
69     for x in parent_ids:
70         if x not in ids_to_check:
71             ids_to_check.append(x)
72             trigger = True
73
74     if trigger:
75         ids_to_check = _get_parents(cr, uid, ids_to_check)
76
77     return ids_to_check
78
79
80 def test_prof(cr, uid, seg_id, pid, answers_ids = []):
81
82     """ return True if the partner pid fetch the segmentation rule seg_id
83         @param cr: the current row, from the database cursor,
84         @param uid: the current user’s ID for security checks,
85         @param seg_id: Segmentaion's ID
86         @param pid: partner's ID
87         @param answers_ids: Answers's IDs
88     """
89
90     ids_to_check = _get_parents(cr, uid, [seg_id])
91     [yes_answers, no_answers] = _get_answers(cr, uid, ids_to_check)
92     temp = True
93     for y_ans in yes_answers:
94         if y_ans not in answers_ids:
95             temp = False
96             break
97     if temp:
98         for ans in answers_ids:
99             if ans in no_answers:
100                 temp = False
101                 break
102     if temp:
103         return True
104     return False
105
106
107 def _recompute_categ(self, cr, uid, pid, answers_ids):
108     """ Recompute category
109         @param self: The object pointer
110         @param cr: the current row, from the database cursor,
111         @param uid: the current user’s ID for security checks,
112         @param pid: partner's ID
113         @param answers_ids: Answers's IDs
114     """
115
116     ok =  []
117     cr.execute('''
118         select r.category_id
119         from res_partner_category_rel r left join crm_segmentation s on (r.category_id = s.categ_id)
120         where r.partner_id = %s and (s.exclusif = false or s.exclusif is null)
121         ''', (pid,))
122     for x in cr.fetchall():
123         ok.append(x[0])
124
125     query = '''
126         select id, categ_id
127         from crm_segmentation
128         where profiling_active = true'''
129     if ok != []:
130         query = query +''' and categ_id not in(%s)'''% ','.join([str(i) for i in ok ])
131     query = query + ''' order by id '''
132
133     cr.execute(query)
134     segm_cat_ids = cr.fetchall()
135
136     for (segm_id, cat_id) in segm_cat_ids:
137         if test_prof(cr, uid, segm_id, pid, answers_ids):
138             ok.append(cat_id)
139     return ok
140
141
142 class question(osv.osv):
143     """ Question """
144
145     _name="crm_profiling.question"
146     _description= "Question"
147
148     _columns={
149         'name': fields.char("Question",size=128, required=True),
150         'answers_ids': fields.one2many("crm_profiling.answer","question_id","Avalaible answers",),
151         }
152
153 question()
154
155
156 class questionnaire(osv.osv):
157     """ Questionnaire """
158
159     _name="crm_profiling.questionnaire"
160     _description= "Questionnaire"
161
162     def build_form(self, cr, uid, data, context=None):
163         """
164             @param self: The object pointer
165             @param cr: the current row, from the database cursor,
166             @param uid: the current user’s ID for security checks,
167             @param data: Get Data
168             @param context: A standard dictionary for contextual values """
169         
170         if not context:
171             context = {}
172         query = """
173         select name, id
174         from crm_profiling_question
175         where id in ( select question from profile_questionnaire_quest_rel where questionnaire = %s)"""
176         res = cr.execute(query, (data['form']['questionnaire_name'],))
177         result = cr.fetchall()
178         quest_fields={}
179         quest_form='''<?xml version="1.0"?>
180             <form string="%s">''' % _('Questionnaire')
181         for name, oid in result:
182             quest_form = quest_form + '<field name="quest_form%d"/><newline/>' % (oid,)
183             quest_fields['quest_form%d' % (oid,)] = {'string': name, 'type': 'many2one', \
184                         'relation': 'crm_profiling.answer', 'domain': [('question_id','=',oid)] }
185         quest_form = quest_form + '''</form>'''
186         return quest_form, quest_fields
187
188     _columns = {
189         'name': fields.char("Questionnaire",size=128, required=True),
190         'description':fields.text("Description", required=True),
191         'questions_ids': fields.many2many('crm_profiling.question','profile_questionnaire_quest_rel',\
192                                 'questionnaire', 'question', "Questions"),
193     }
194
195 questionnaire()
196
197
198 class answer(osv.osv):
199     _name="crm_profiling.answer"
200     _description="Answer"
201     _columns={
202         "name": fields.char("Answer",size=128, required=True),
203         "question_id": fields.many2one('crm_profiling.question',"Question"),
204         }
205 answer()
206
207
208 class partner(osv.osv):
209     _inherit="res.partner"
210     _columns={
211         "answers_ids": fields.many2many("crm_profiling.answer","partner_question_rel",\
212                                 "partner","answer","Answers"),
213         }
214
215     def _questionnaire_compute(self, cr, uid, data, context=None):
216         """
217             @param self: The object pointer
218             @param cr: the current row, from the database cursor,
219             @param uid: the current user’s ID for security checks,
220             @param data: Get Data
221             @param context: A standard dictionary for contextual values """
222
223         temp = []
224         if not context:
225             context = {}
226         for x in data['form']:
227             if x.startswith("quest_form") and data['form'][x] != 0 :
228                 temp.append(data['form'][x])
229
230         query = "select answer from partner_question_rel where partner=%s"
231         cr.execute(query, (data['id'],))
232         for x in cr.fetchall():
233             temp.append(x[0])
234
235         self.write(cr, uid, [data['id']], {'answers_ids':[[6, 0, temp]]}, context=context)
236         return {}
237
238
239     def write(self, cr, uid, ids, vals, context=None):
240         """
241             @param self: The object pointer
242             @param cr: the current row, from the database cursor,
243             @param uid: the current user’s ID for security checks,
244             @param ids: List of crm profiling’s IDs
245             @param context: A standard dictionary for contextual values """
246
247         if not context:
248             context={}
249         if 'answers_ids' in vals:
250             vals['category_id']=[[6, 0, _recompute_categ(self, cr, uid, ids[0], vals['answers_ids'][0][2])]]
251         return super(partner, self).write(cr, uid, ids, vals, context=context)
252
253 partner()
254
255
256 class crm_segmentation(osv.osv):
257     """ CRM Segmentation """
258
259     _inherit="crm.segmentation"
260     _columns={
261         "answer_yes": fields.many2many("crm_profiling.answer","profile_question_yes_rel",\
262                             "profile","answer","Included Answers"),
263         "answer_no": fields.many2many("crm_profiling.answer","profile_question_no_rel",\
264                             "profile","answer","Excluded Answers"),
265         'parent_id': fields.many2one('crm.segmentation', 'Parent Profile'),
266         'child_ids': fields.one2many('crm.segmentation', 'parent_id', 'Child Profiles'),
267         'profiling_active': fields.boolean('Use The Profiling Rules', help='Check\
268                              this box if you want to use this tab as part of the \
269                              segmentation rule. If not checked, the criteria beneath will be ignored')
270         }
271
272     _constraints = [
273         (orm.orm.check_recursion, 'Error ! You can not create recursive profiles.', ['parent_id'])
274     ]
275
276     def process_continue(self, cr, uid, ids, start=False):
277         """
278             @param self: The object pointer
279             @param cr: the current row, from the database cursor,
280             @param uid: the current user’s ID for security checks,
281             @param ids: List of crm segmentation’s IDs """
282
283         categs = self.read(cr,uid,ids,['categ_id','exclusif','partner_id', \
284                             'sales_purchase_active', 'profiling_active'])
285         for categ in categs:
286             if start:
287                 if categ['exclusif']:
288                     cr.execute('delete from res_partner_category_rel where \
289                             category_id=%s', (categ['categ_id'][0],))
290
291             id = categ['id']
292
293             cr.execute('select id from res_partner order by id ')
294             partners = [x[0] for x in cr.fetchall()]
295
296             if categ['sales_purchase_active']:
297                 to_remove_list=[]
298                 cr.execute('select id from crm_segmentation_line where segmentation_id=%s', (id,))
299                 line_ids = [x[0] for x in cr.fetchall()]
300
301                 for pid in partners:
302                     if (not self.pool.get('crm.segmentation.line').test(cr, uid, line_ids, pid)):
303                         to_remove_list.append(pid)
304                 for pid in to_remove_list:
305                     partners.remove(pid)
306
307             if categ['profiling_active']:
308                 to_remove_list = []
309                 for pid in partners:
310
311                     cr.execute('select distinct(answer) from partner_question_rel where partner=%s',(pid,))
312                     answers_ids = [x[0] for x in cr.fetchall()]
313
314                     if (not test_prof(cr, uid, id, pid, answers_ids)):
315                         to_remove_list.append(pid)
316                 for pid in to_remove_list:
317                     partners.remove(pid)
318
319             for partner_id in partners:
320                 cr.execute('insert into res_partner_category_rel (category_id,partner_id) values (%s,%s)', (categ['categ_id'][0],partner_id))
321
322             self.write(cr, uid, [id], {'state':'not running', 'partner_id':0})
323         return True
324
325 crm_segmentation()
326
327 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
328