*improvement and bugfixes
[odoo/odoo.git] / addons / event / event.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2007 TINY SPRL. (http://tiny.be) All Rights Reserved.
5 #
6 #
7 # WARNING: This program as such is intended to be used by professional
8 # programmers who take the whole responsability of assessing all potential
9 # consequences resulting from its eventual inadequacies and bugs
10 # End users who are looking for a ready-to-use solution with commercial
11 # garantees and support are strongly adviced to contract a Free Software
12 # Service Company
13 #
14 # This program is Free Software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27 #
28 ##############################################################################
29
30 from osv import fields, osv
31 import time
32 import netsvc
33 import pooler
34 import tools
35
36 class crm_case_log(osv.osv):
37     _inherit = 'crm.case.log'
38     _description = 'crm.case.log'
39     def create(self, cr, uid, vals, *args, **kwargs):
40             if not 'name' in vals:
41                 vals['name']='Historize'
42             return super(osv.osv,self).create(cr, uid, vals, *args, **kwargs)
43     _defaults = {
44         'user_id': lambda self,cr,uid,context: uid,
45     }
46 crm_case_log()
47
48 class one2many_mod_task(fields.one2many):
49     def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
50         if not context:
51             context = {}
52         if not values:
53                 values = {}
54         res = {}
55         for id in ids:
56             res[id] = []
57         for id in ids:
58             query = "select project_id from event_event where id = %i" %id
59             cr.execute(query)
60             project_ids = [ x[0] for x in cr.fetchall()]
61             ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',project_ids),('state','<>','done')], limit=self._limit)
62             for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
63                 res[id].append( r['id'] )
64         return res
65
66 class event_type(osv.osv):
67     _name = 'event.type'
68     _description= 'Event type'
69     _columns = {
70         'name': fields.char('Event type', size=64, required=True),
71     }
72 event_type()
73
74 class event(osv.osv):
75     _name = 'event.event'
76     _description = 'Event'
77     _inherits = {'crm.case.section': 'section_id'}
78     _order = 'date_begin'
79
80     def copy(self, cr, uid, id, default=None, context=None):
81         return super(event, self).copy(cr, uid,id, default={'code': self.pool.get('ir.sequence').get(cr, uid, 'event.event'),'state':'draft'})
82
83     def button_draft(self, cr, uid, ids, context={}):
84         return self.write(cr, uid, ids, {'state':'draft'})
85
86     def button_cancel(self, cr, uid, ids, context={}):
87         return self.write(cr, uid, ids, {'state':'cancel'})
88
89     def button_done(self, cr, uid, ids, context={}):
90         return self.write(cr, uid, ids, {'state':'done'})
91
92     def button_confirm(self, cr, uid, ids, context={}):
93         for eve in self.browse(cr, uid, ids):
94             if eve.mail_auto_confirm:
95                 #send reminder that will confirm the event for all the people that were already confirmed
96                 reg_ids = self.pool.get('event.registration').search(cr, uid, [('event_id','=',eve.id),('state','not in',['draft','cancel'])])
97                 if reg_ids:
98                     self.pool.get('event.registration').mail_user_confirm(cr, uid, reg_ids)
99         return self.write(cr, uid, ids, {'state':'confirm'})
100
101     def _get_register(self, cr, uid, ids, name, args, context=None):
102         res={}
103         for event in self.browse(cr, uid, ids, context):
104             query = """select sum(nb_register) from crm_case c left join crm_case_section s on (c.section_id=s.id) right join event_event e on (e.section_id=s.id) right join event_registration r on (r.case_id=c.id) where e.section_id = %s and c.state in ('open','done')""" % event.section_id.id
105             cr.execute(query)
106             res2 = cr.fetchone()
107             if res2 and res2[0]:
108                 res[event.id] = res2[0]
109             else:
110                 res[event.id] = 0
111         return res
112
113     def _get_prospect(self, cr, uid, ids, name, args, context=None):
114         res={}
115         for event in self.browse(cr, uid, ids, context):
116             query = """select sum(nb_register) from crm_case c left join crm_case_section s on (c.section_id=s.id) right join event_event e on (e.section_id=s.id) right join event_registration r on (r.case_id=c.id) where e.section_id = %s and c.state = 'draft'""" % event.section_id.id
117             cr.execute(query)
118             res2 = cr.fetchone()
119             if res2 and res2[0]:
120                 res[event.id] = res2[0]
121             else:
122                 res[event.id] = 0
123         return res
124
125     def write(self, cr, uid, ids,vals, *args, **kwargs):
126         res = super(event,self).write(cr, uid, ids,vals, *args, **kwargs)
127         if 'date_begin' in vals and vals['date_begin']:
128             for eve in self.browse(cr, uid, ids):
129                 #change the deadlines of the registration linked to this event
130                 reg_ids = self.pool.get('event.registration').search(cr, uid, [('event_id','=',eve.id)])
131                 if reg_ids:
132                     self.pool.get('event.registration').write(cr, uid, reg_ids, {'date_deadline':vals['date_begin']})
133                 #change the date of the project
134                 if eve.project_id:
135                     self.pool.get('project.project').write(cr, uid, [eve.project_id.id], {'date_end':eve.date_begin})
136
137         #change the description of the registration linked to this event
138         if 'mail_auto_confirm' in vals:
139             if vals['mail_auto_confirm']:
140                 if 'mail_confirm' not in vals: 
141                     for eve in self.browse(cr, uid, ids):
142                         vals['mail_confirm'] = eve.mail_confirm
143             else:
144                 vals['mail_confirm']=False
145         if 'mail_confirm' in vals:
146             for eve in self.browse(cr, uid, ids):
147                 reg_ids = self.pool.get('event.registration').search(cr, uid, [('event_id','=',eve.id)])
148                 if reg_ids:
149                     self.pool.get('event.registration').write(cr, uid, reg_ids, {'description':vals['mail_confirm']})
150         return res
151
152     _columns = {
153         'type': fields.many2one('event.type', 'Type'),
154         'section_id': fields.many2one('crm.case.section', 'Case section', required=True),
155         'register_max': fields.integer('Maximum Registrations'),
156         'register_min': fields.integer('Minimum Registrations'),
157         'register_current': fields.function(_get_register, method=True, string='Confirmed Registrations'),
158         'register_prospect': fields.function(_get_prospect, method=True, string='Unconfirmed Registrations'),
159         'project_id': fields.many2one('project.project', 'Project', readonly=True),
160         'task_ids': one2many_mod_task('project.task', 'project_id', "Project tasks", readonly=True, domain="[('state','&lt;&gt;', 'done')]"),
161         'date_begin': fields.datetime('Beginning date', required=True),
162         'date_end': fields.datetime('Ending date', required=True),
163         'state': fields.selection([('draft','Draft'),('confirm','Confirmed'),('done','Done'),('cancel','Canceled')], 'Status', readonly=True, required=True),
164         'mail_auto_registr':fields.boolean('Mail Auto Register',help='Check this box if you want to use the automatic mailing for new registration'),
165         'mail_auto_confirm':fields.boolean('Mail Auto Confirm',help='Check this box if you want ot use the automatic confirmation emailing or the reminder'),
166         'mail_registr':fields.text('Registration Email',help='This email will be sent when someone subscribes to the event.'),
167         'mail_confirm': fields.text('Confirmation Email', help="This email will be sent when the event gets confimed or when someone subscribes to a confirmed event. This is also the email sent to remind someone about the event."),
168         'budget_id':fields.many2one('account.budget.post','Budget'),
169         'product_id':fields.many2one('product.product','Product', required=True),
170     }
171     _defaults = {
172         'state': lambda *args: 'draft',
173         'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'event.event'),
174         'user_id': lambda self,cr,uid,ctx: uid,
175     }
176 event()
177
178 class event_registration(osv.osv):
179
180     def _history(self, cr, uid,ids,keyword, history=False, email=False, context={}):
181         for case in ids:
182             data = {
183                 'name': keyword,
184                 'som': case.som.id,
185                 'canal_id': case.canal_id.id,
186                 'user_id': uid,
187                 'case_id': case.case_id.id
188             }
189             obj = self.pool.get('crm.case.log')
190             obj.create(cr, uid, data, context)
191         return True
192
193     def button_reg_close(self, cr, uid, ids, *args):
194         self.write(cr, uid, ids, {'state':'done',})
195         cases = self.browse(cr, uid, ids)
196         self._history(cr, uid, cases, 'Done', history=True)
197         return True
198
199     def button_reg_cancel(self, cr, uid, ids, *args):
200         self.write(cr, uid, ids, {'state':'cancel',})
201         cases = self.browse(cr, uid, ids)
202         self._history(cr, uid, cases, 'Cancel', history=True)
203         return True
204
205     def create(self, cr, uid, *args, **argv):
206         event = self.pool.get('event.event').browse(cr, uid, args[0]['event_id'], None)
207         args[0]['section_id']= event.section_id.id
208         args[0]['date_deadline']= event.date_begin
209         args[0]['description']= event.mail_confirm
210         res = super(event_registration, self).create(cr, uid, *args, **argv)
211         self._history(cr, uid,self.browse(cr, uid, [res]), 'Created', history=True)
212         return res
213
214     def write(self, cr, uid, *args, **argv):
215         if 'event_id' in args[1]:
216             event = self.pool.get('event.event').browse(cr, uid, args[1]['event_id'], None)
217             args[1]['section_id']= event.section_id.id
218             args[1]['date_deadline']= event.date_begin
219             args[1]['description']= event.mail_confirm
220         return super(event_registration, self).write(cr, uid, *args, **argv)
221
222     def mail_user_confirm(self,cr,uid,ids):
223         reg_ids=self.browse(cr,uid,ids)
224         for reg_id in reg_ids:
225             src = reg_id.event_id.reply_to or False
226             dest = [reg_id.email_from]
227             if reg_id.email_cc:
228                 dest += [reg_id.email_cc]
229             if dest and src:
230                 tools.email_send(src, dest,'Auto Confirmation: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_confirm, tinycrm = str(reg_id.case_id.id))
231             if not src:
232                 raise osv.except_osv(_('Error!'), _('You must define a reply-to address in order to mail the participant. You can do this in the Mailing tab of your event. Note that this is also the place where you can configure your event to not send emails automaticly while registering'))
233         return False
234
235     def mail_user(self,cr,uid,ids):
236         reg_ids=self.browse(cr,uid,ids)
237         for reg_id in reg_ids:
238             src = reg_id.event_id.reply_to or False
239             dest = [reg_id.email_from]
240             if reg_id.email_cc:
241                 dest += [reg_id.email_cc]
242             if reg_id.event_id.mail_auto_confirm or reg_id.event_id.mail_auto_registr:
243                 if dest and src:
244                     if reg_id.event_id.state in ['draft', 'fixed', 'open','confirm','running'] and reg_id.event_id.mail_auto_registr:
245                         tools.email_send(src, dest,'Auto Registration: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_registr, tinycrm = str(reg_id.case_id.id))
246                     if (reg_id.event_id.state in ['confirm','running']) and reg_id.event_id.mail_auto_confirm:
247                         tools.email_send(src, dest,'Auto Confirmation: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_confirm, tinycrm = str(reg_id.case_id.id))
248                 if not src:
249                     raise osv.except_osv(_('Error!'), _('You must define a reply-to address in order to mail the participant. You can do this in the Mailing tab of your event. Note that this is also the place where you can configure your event to not send emails automaticly while registering'))
250         return False
251
252     _name= 'event.registration'
253     _description = 'Event Registration'
254     _inherits = {'crm.case': 'case_id'}
255     _columns = {
256         'case_id':fields.many2one('crm.case','Case'),
257         'nb_register': fields.integer('Number of Registration', readonly=True, states={'draft':[('readonly',False)]}),
258         'event_id':fields.many2one('event.event', 'Event Related', required=True),
259         "partner_invoice_id":fields.many2one('res.partner', 'Partner Invoiced'),
260         "contact_id":fields.many2one('res.partner.contact', 'Partner Contact'), #TODO: filter only the contacts that have a function into the selected partner_id
261         "unit_price": fields.float('Unit Price'),
262         "badge_title":fields.char('Badge Title',size=128),
263         "badge_name":fields.char('Badge Name',size=128),
264         "badge_partner":fields.char('Badge Partner',size=128),
265         "invoice_label":fields.char("Label Invoice",size=128,required=True),
266         "tobe_invoiced":fields.boolean("To be Invoiced"),
267         "invoice_id":fields.many2one("account.invoice","Invoice"),
268     }
269     _defaults = {
270         'nb_register': lambda *a: 1,
271         'tobe_invoiced' : lambda *a: True,
272         'name': lambda *a: 'Registration'
273     }
274
275     def onchange_badge_name(self, cr, uid, ids, badge_name):
276         data ={}
277         if not badge_name:
278             return data
279         data['name'] = 'Registration: ' + badge_name
280         return {'value':data}
281
282     def onchange_contact_id(self, cr, uid, ids, contact, partner):
283         data ={}
284         if not contact:
285             return data
286         contact_id = self.pool.get('res.partner.contact').browse(cr, uid, contact)
287         data['badge_name'] = contact_id.name
288         data['badge_title'] = contact_id.title
289         if partner:
290             partner_addresses = self.pool.get('res.partner.address').search(cr, uid, [('partner_id','=',partner)])
291             job_id = self.pool.get('res.partner.job').search(cr, uid, [('contact_id','=',contact),('address_id','in',partner_addresses)])[0]
292             data['email_from'] = self.pool.get('res.partner.job').browse(cr, uid, job_id).email
293         d = self.onchange_badge_name(cr, uid, ids,data['badge_name'])
294         data.update(d['value'])
295
296         return {'value':data}
297
298     def onchange_event(self, cr, uid, ids, event_id, partner_invoice_id):
299         context={}
300         if not event_id:
301             return {'value':{'unit_price' : False ,'invoice_label' : False }}
302         data_event =  self.pool.get('event.event').browse(cr,uid,event_id)
303         if data_event.product_id:
304             if not partner_invoice_id:
305                 unit_price=self.pool.get('product.product').price_get(cr, uid, [data_event.product_id.id],context=context)[data_event.product_id.id]
306                 return {'value':{'unit_price' : unit_price , 'invoice_label' : data_event.product_id.name}}
307             data_partner = self.pool.get('res.partner').browse(cr,uid,partner_invoice_id)
308             context.update({'partner_id':data_partner})
309             unit_price = self.pool.get('product.product')._product_price(cr, uid, [data_event.product_id.id], False, False, {'pricelist':data_partner.property_product_pricelist.id})[data_event.product_id.id]
310             return {'value':{'unit_price' :unit_price , 'invoice_label' : data_event.product_id.name}}
311         return {'value':{'unit_price' : False,'invoice_label' : False}}
312
313
314     def onchange_partner_id(self, cr, uid, ids, part, event_id, email=False):
315         data={}
316         data['badge_partner'] = data['contact_id'] = data['partner_invoice_id'] = data['email_from'] = data['badge_title'] = data['badge_name'] = False
317         if not part:
318             return {'value':data}
319         data['partner_invoice_id']=part
320         # this calls onchange_partner_invoice_id
321         d = self.onchange_partner_invoice_id(cr, uid, ids, event_id,part)
322         # this updates the dictionary
323         data.update(d['value'])
324         addr = self.pool.get('res.partner').address_get(cr, uid, [part])
325         if addr:
326             if addr.has_key('default'):
327                 job_ids = self.pool.get('res.partner.job').search(cr, uid, [('address_id','=',addr['default'])])
328                 if job_ids:
329                     data['contact_id'] = self.pool.get('res.partner.job').browse(cr, uid, job_ids[0]).contact_id.id
330                     d = self.onchange_contact_id(cr, uid, ids, data['contact_id'],part)
331                     data.update(d['value'])
332         partner_data = self.pool.get('res.partner').browse(cr, uid, part)
333         data['badge_partner'] = partner_data.name
334         return {'value':data}
335
336     def onchange_partner_invoice_id(self, cr, uid, ids, event_id, partner_invoice_id):
337         data={}
338         context={}
339         data['unit_price']=False
340         if not event_id:
341             return {'value':data}
342         data_event =  self.pool.get('event.event').browse(cr,uid,event_id)
343
344         if data_event.product_id:
345             if not partner_invoice_id:
346                 data['unit_price']=self.pool.get('product.product').price_get(cr, uid, [data_event.product_id.id],context=context)[data_event.product_id.id]
347                 return {'value':data}
348             data_partner = self.pool.get('res.partner').browse(cr,uid,partner_invoice_id)
349             context.update({'partner_id':data_partner})
350             data['unit_price'] = self.pool.get('product.product')._product_price(cr, uid, [data_event.product_id.id], False, False, {'pricelist':data_partner.property_product_pricelist.id})[data_event.product_id.id]
351             return {'value':data}
352         return {'value':data}
353
354     def onchange_categ_id(self, cr, uid, ids, categ, context={}):
355         if not categ:
356             return {'value':{}}
357         cat = self.pool.get('crm.case.categ').browse(cr, uid, categ, context).probability
358         return {'value':{'probability':cat}}
359
360     def _map_ids(self,method,cr, uid, ids, *args, **argv):
361         case_data = self.browse(cr,uid,ids)
362         new_ids=[]
363         for case in case_data:
364             new_ids.append(case.case_id.id)
365         return getattr(self.pool.get('crm.case'),method)(cr, uid, new_ids, *args, **argv)
366
367
368     def case_close(self,cr, uid, ids, *args, **argv):
369         return self._map_ids('case_close',cr,uid,ids,*args,**argv)
370     def case_escalate(self,cr, uid, ids, *args, **argv):
371         return self._map_ids('case_escalate',cr,uid,ids,*args,**argv)
372     def case_open(self,cr, uid, ids, *args, **argv):
373         return self._map_ids('case_open',cr,uid,ids,*args,**argv)
374     def case_cancel(self,cr, uid, ids, *args, **argv):
375         return self._map_ids('case_cancel',cr,uid,ids,*args,**argv)
376     def case_pending(self,cr, uid, ids, *args, **argv):
377         return self._map_ids('case_pending',cr,uid,ids,*args,**argv)
378     def case_reset(self,cr, uid, ids, *args, **argv):
379         return self._map_ids('case_reset',cr,uid,ids,*args,**argv)
380     def case_log(self,cr, uid, ids, *args, **argv):
381         return self._map_ids('case_log',cr,uid,ids,*args,**argv)
382     def case_log_reply(self,cr, uid, ids, *args, **argv):
383         return self._map_ids('case_log_reply',cr,uid,ids,*args,**argv)
384     def add_reply(self,cr, uid, ids, *args, **argv):
385         return self._map_ids('add_reply',cr,uid,ids,*args,**argv)
386     def remind_partner(self,cr, uid, ids, *args, **argv):
387         return self._map_ids('remind_partner',cr,uid,ids,*args,**argv)
388     def remind_user(self,cr, uid, ids, *args, **argv):
389         return self._map_ids('remind_user',cr,uid,ids,*args,**argv)
390
391
392 event_registration()
393
394
395
396 class report_event_registration(osv.osv):
397     _name = "report.event.registration"
398     _description = "Events on registrations"
399     _auto = False
400     _columns = {
401         'name': fields.char('Event',size=20),
402         'date_begin': fields.datetime('Beginning date', required=True),
403         'date_end': fields.datetime('Ending date', required=True),
404         'draft_state': fields.integer('Draft Registration',size=20),
405         'confirm_state': fields.integer('Confirm Registration',size=20),
406         'register_max': fields.integer('Maximum Registrations'),
407     }
408     def init(self, cr):
409         cr.execute("""
410             create or replace view report_event_registration as (
411                 select
412                 e.id as id,
413                 c.name as name,
414                 e.date_begin as date_begin,
415                 e.date_end as date_end,
416                 (SELECT sum(nb_register) FROM event_registration x , crm_case c  WHERE x.case_id=c.id and c.section_id=e.section_id and state in ('draft')) as draft_state,
417                 (SELECT sum(nb_register) FROM event_registration x , crm_case c  WHERE x.case_id=c.id and c.section_id=e.section_id and state in ('open')) as confirm_state,
418                 e.register_max as register_max
419                 from
420                 event_event e,crm_case_section c
421                 where
422                 e.section_id=c.id
423                 )""")
424
425 report_event_registration()
426
427 class report_event_type_registration(osv.osv):
428     _name = "report.event.type.registration"
429     _description = "Event type on registration"
430     _auto = False
431     _columns = {
432         'name': fields.char('Event Type',size=20),
433         'nbevent':fields.integer('Number Of Events'),
434         'draft_state': fields.integer('Draft Registrations',size=20),
435         'confirm_state': fields.integer('Confirm Registrations',size=20),
436         }
437     def init(self, cr):
438         cr.execute("""
439             create or replace view report_event_type_registration as (
440                 select
441                 count(t.id) as id,
442                 t.name as name,
443                 (select sum(nb_register) from event_registration r , crm_case c , event_event e  where c.section_id=e.section_id and r.case_id=c.id and c.state='draft' and e.type=t.id ) as draft_state ,
444                 (select sum(nb_register) from event_registration r , crm_case c , event_event e  where c.section_id=e.section_id and r.case_id=c.id and c.state='open' and e.type=t.id ) as  confirm_state,
445                 count(t.id) as nbevent
446                 from
447                     event_event e
448                 inner join
449                     crm_case_section c1 on (e.section_id=c1.id)
450                 inner join
451                     event_type t on (e.type=t.id)
452                 group by
453                     t.name,t.id
454
455             )""")
456 report_event_type_registration()
457
458
459 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
460