1354eac467c46427da395f856caa98fe9ed4843b
[odoo/odoo.git] / addons / event / event.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 import time
24 import netsvc
25 import pooler
26 import tools
27 from tools.translate import _
28 from crm import crm
29
30
31 class event_type(osv.osv):
32     _name = 'event.type'
33     _description= 'Event type'
34     _columns = {
35         'name': fields.char('Event type', size=64, required=True), 
36     }
37 event_type()
38
39 class event(osv.osv):
40     _name = 'event.event'
41     _description = 'Event'
42     _inherits = {'crm.case.section': 'section_id'}
43     _order = 'date_begin'
44
45     def copy(self, cr, uid, id, default=None, context=None):
46         return super(event, self).copy(cr, uid, id, default={'code': self.pool.get('ir.sequence').get(cr, uid, 'event.event'), 'state': 'draft'})
47
48     def button_draft(self, cr, uid, ids, context={}):
49         return self.write(cr, uid, ids, {'state': 'draft'})
50
51     def button_cancel(self, cr, uid, ids, context={}):
52         return self.write(cr, uid, ids, {'state': 'cancel'})
53
54     def button_done(self, cr, uid, ids, context={}):
55         return self.write(cr, uid, ids, {'state': 'done'})
56
57     def button_confirm(self, cr, uid, ids, context={}):
58         for eve in self.browse(cr, uid, ids):
59             if eve.mail_auto_confirm:
60                 #send reminder that will confirm the event for all the people that were already confirmed
61                 reg_ids = self.pool.get('event.registration').search(cr, uid, [('event_id', '=', eve.id), ('state', 'not in', ['draft', 'cancel'])])
62                 if reg_ids:
63                     self.pool.get('event.registration').mail_user_confirm(cr, uid, reg_ids)
64         return self.write(cr, uid, ids, {'state': 'confirm'})
65
66     def _get_register(self, cr, uid, ids, name, args, context=None):
67         res={}
68         for event in self.browse(cr, uid, ids, context):
69             res[event.id] = {}
70             state = 'draft'
71             if name[0] == 'register_current':
72                 state = 'open'
73             query = """SELECT sum(r.nb_register) 
74                         from event_registration r 
75                         where state=%s and event_id=%s"""
76
77             cr.execute(query, (state, event.id, ))
78             res2 = cr.fetchone()
79             if res2 and res2[0]:
80                 res[event.id][name[0]] = res2[0]
81             else:
82                 res[event.id][name[0]] = 0
83         return res
84
85     def write(self, cr, uid, ids, vals, *args, **kwargs):
86         res = super(event, self).write(cr, uid, ids, vals, *args, **kwargs)
87         if 'date_begin' in vals and vals['date_begin']:
88             for eve in self.browse(cr, uid, ids):
89                 #change the deadlines of the registration linked to this event
90                 reg_ids = self.pool.get('event.registration').search(cr, uid, [('event_id', '=', eve.id)])
91                 if reg_ids:
92                     self.pool.get('event.registration').write(cr, uid, reg_ids, {'date_deadline': vals['date_begin']})
93
94         #change the description of the registration linked to this event
95         if 'mail_auto_confirm' in vals:
96             if vals['mail_auto_confirm']:
97                 if 'mail_confirm' not in vals:
98                     for eve in self.browse(cr, uid, ids):
99                         vals['mail_confirm'] = eve.mail_confirm
100             else:
101                 vals['mail_confirm']=False
102         if 'mail_confirm' in vals:
103             for eve in self.browse(cr, uid, ids):
104                 reg_ids = self.pool.get('event.registration').search(cr, uid, [('event_id', '=', eve.id)])
105                 if reg_ids:
106                     self.pool.get('event.registration').write(cr, uid, reg_ids, {'description': vals['mail_confirm']})
107         return res
108
109     _columns = {
110         'type': fields.many2one('event.type', 'Type'), 
111         'section_id': fields.many2one('crm.case.section', 'Case section', required=True), 
112         'register_max': fields.integer('Maximum Registrations'), 
113         'register_min': fields.integer('Minimum Registrations'), 
114         'register_current': fields.function(_get_register, method=True, string='Confirmed Registrations', multi='register_current'), 
115         'register_prospect': fields.function(_get_register, method=True, string='Unconfirmed Registrations', multi='register_prospect'), 
116         'date_begin': fields.datetime('Beginning date', required=True), 
117         'date_end': fields.datetime('Ending date', required=True), 
118         'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, required=True, 
119                                   help='If event is created, the state is \'Draft\'.\n If event is confirmed for the particular dates the state is set to \'Confirmed\'.\
120                                   \nIf the event is over, the state is set to \'Done\'.\n If event is cancelled the state is set to \'Cancelled\'.'), 
121         'mail_auto_registr': fields.boolean('Mail Auto Register', help='Check this box if you want to use the automatic mailing for new registration'), 
122         'mail_auto_confirm': fields.boolean('Mail Auto Confirm', help='Check this box if you want ot use the automatic confirmation emailing or the reminder'), 
123         'mail_registr': fields.text('Registration Email', help='This email will be sent when someone subscribes to the event.'), 
124         '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."), 
125         'product_id': fields.many2one('product.product', 'Product', required=True), 
126     }
127
128     _defaults = {
129         'state': lambda *args: 'draft', 
130         'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'event.event'), 
131         'user_id': lambda self, cr, uid, ctx: uid, 
132     }
133
134 event()
135
136 class event_registration(osv.osv):
137
138     def check_confirm(self, cr, uid, ids, context):
139         mod_obj = self.pool.get('ir.model.data')
140         current_registration = self.browse(cr, uid, [ids[0]])[0]
141         total_confirmed = current_registration.event_id.register_current + current_registration.nb_register
142         if total_confirmed <= current_registration.event_id.register_max or current_registration.event_id.register_max == 0:
143             self.write(cr, uid, [ids[0]], {'state': 'open'}, context=context)
144             self._history(cr, uid, [ids[0]], 'Open', history=True)
145             self.mail_user(cr, uid, [ids[0]])
146             return True
147         else:
148             model_data_ids = mod_obj.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_event_confirm_registration')], context=context)
149             resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
150             context.update({'reg_id': ids[0]})
151             return {
152                 'name': _('Confirm Registration'), 
153                 'context': context, 
154                 'view_type': 'form', 
155                 'view_mode': 'tree,form', 
156                 'res_model': 'event.confirm.registration', 
157                 'views': [(resource_id, 'form')], 
158                 'type': 'ir.actions.act_window', 
159                 'target': 'new', 
160                 'nodestroy': True
161             }
162
163     def _history(self, cr, uid, ids, keyword, history=False, email=False, context={}):
164         for case in self.browse(cr, uid, ids):
165             data = {
166                 'name': keyword, 
167                 'som': case.som.id, 
168                 'canal_id': case.canal_id.id, 
169                 'user_id': uid, 
170             }
171             obj = self.pool.get('mailgate.message')
172             obj.create(cr, uid, data, context=context)
173         return True
174
175     def button_reg_close(self, cr, uid, ids, *args):
176         self.write(cr, uid, ids, {'state': 'done', })
177         self._history(cr, uid, ids, 'Done', history=True)
178         return True
179
180     def button_reg_cancel(self, cr, uid, ids, *args):
181         self.write(cr, uid, ids, {'state': 'cancel', })
182         self._history(cr, uid, ids, 'Cancel', history=True)
183         return True
184
185     def create(self, cr, uid, *args, **argv):
186         event = self.pool.get('event.event').browse(cr, uid, args[0]['event_id'], None)
187         args[0]['section_id']= event.section_id.id
188         args[0]['date_deadline']= event.date_begin
189         args[0]['description']= event.mail_confirm
190         res = super(event_registration, self).create(cr, uid, *args, **argv)
191         self._history(cr, uid, [res], 'Created', history=True)
192         return res
193
194     def write(self, cr, uid, *args, **argv):
195         if 'event_id' in args[1]:
196             event = self.pool.get('event.event').browse(cr, uid, args[1]['event_id'], None)
197             args[1]['section_id']= event.section_id.id
198             args[1]['date_deadline']= event.date_begin
199             args[1]['description']= event.mail_confirm
200         return super(event_registration, self).write(cr, uid, *args, **argv)
201
202     def mail_user_confirm(self, cr, uid, ids):
203         reg_ids = self.browse(cr, uid, ids)
204         for reg_id in reg_ids:
205             src = reg_id.event_id.reply_to or False
206             dest = []
207             if reg_id.email_from:
208                 dest += [reg_id.email_from]
209             if reg_id.email_cc:
210                 dest += [reg_id.email_cc]
211             if dest and src:
212                 tools.email_send(src, dest, 'Auto Confirmation: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_confirm, openobject_id = str(reg_id.id))
213             if not src:
214                 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'))
215         return False
216
217     def mail_user(self, cr, uid, ids):
218         reg_ids = self.browse(cr, uid, ids)
219         for reg_id in reg_ids:
220             src = reg_id.event_id.reply_to or False
221             dest = []
222             if reg_id.email_from:
223                 dest += [reg_id.email_from]
224             if reg_id.email_cc:
225                 dest += [reg_id.email_cc]
226             if reg_id.event_id.mail_auto_confirm or reg_id.event_id.mail_auto_registr:
227                 if dest and src:
228                     if reg_id.event_id.state in ['draft', 'fixed', 'open', 'confirm', 'running'] and reg_id.event_id.mail_auto_registr:
229                         tools.email_send(src, dest, 'Auto Registration: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_registr, openobject_id = str(reg_id.id))
230                     if (reg_id.event_id.state in ['confirm', 'running']) and reg_id.event_id.mail_auto_confirm:
231                         tools.email_send(src, dest, 'Auto Confirmation: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_confirm, openobject_id = str(reg_id.id))
232                 if not src:
233                     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'))
234         return False
235
236     def _create_invoice_lines(self, cr, uid, ids, vals):
237         return self.pool.get('account.invoice.line').create(cr, uid, vals)
238
239     _name= 'event.registration'
240     _description = 'Event Registration'
241     _inherit = 'crm.meeting'
242
243     _columns = {
244         'email_cc': fields.text('Watchers Emails', size=252 , help="These \
245 people will receive a copy of the future communication between partner \
246 and users by email"),
247         'nb_register': fields.integer('Number of Registration', readonly=True, states={'draft': [('readonly', False)]}), 
248         'event_id': fields.many2one('event.event', 'Event Related', required=True), 
249         "partner_invoice_id": fields.many2one('res.partner', 'Partner Invoiced'), 
250         "contact_id": fields.many2one('res.partner.contact', 'Partner Contact'), #TODO: filter only the contacts that have a function into the selected partner_id
251         "unit_price": fields.float('Unit Price'), 
252         "badge_title": fields.char('Badge Title', size=128), 
253         "badge_name": fields.char('Badge Name', size=128), 
254         "badge_partner": fields.char('Badge Partner', size=128), 
255         "invoice_label": fields.char("Label Invoice", size=128, required=True), 
256         "tobe_invoiced": fields.boolean("To be Invoiced"), 
257         "invoice_id": fields.many2one("account.invoice", "Invoice"), 
258         'date_closed': fields.datetime('Closed', readonly=True), 
259         'ref': fields.reference('Reference', selection=crm._links_get, size=128), 
260         'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), 
261         'canal_id': fields.many2one('res.partner.canal', 'Channel', help="The channels represent the different communication modes available with the customer." \
262                                                                         " With each commercial opportunity, you can indicate the canall which is this opportunity source."), 
263         'som': fields.many2one('res.partner.som', 'State of Mind', help="The minds states allow to define a value scale which represents" \
264                                                                        "the partner mentality in relation to our services.The scale has" \
265                                                                        "to be created with a factor for each level from 0 (Very dissatisfied) to 10 (Extremely satisfied)."), 
266     }
267     
268     _defaults = {
269         'nb_register': lambda *a: 1, 
270         'tobe_invoiced': lambda *a: True, 
271         'name': lambda *a: 'Registration', 
272     }
273
274     def onchange_badge_name(self, cr, uid, ids, badge_name):
275         data ={}
276         if not badge_name:
277             return data
278         data['name'] = 'Registration: ' + badge_name
279         return {'value': data}
280
281     def onchange_contact_id(self, cr, uid, ids, contact, partner):
282         data ={}
283         if not contact:
284             return data
285         contact_id = self.pool.get('res.partner.contact').browse(cr, uid, contact)
286         data['badge_name'] = contact_id.name
287         data['badge_title'] = contact_id.title
288         if partner:
289             partner_addresses = self.pool.get('res.partner.address').search(cr, uid, [('partner_id', '=', partner)])
290             job_ids = self.pool.get('res.partner.job').search(cr, uid, [('contact_id', '=', contact), ('address_id', 'in', partner_addresses)])
291             if job_ids:
292                 data['email_from'] = self.pool.get('res.partner.job').browse(cr, uid, job_ids[0]).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 event_registration()
361
362
363
364 class report_event_registration(osv.osv):
365     _name = "report.event.registration"
366     _description = "Events on registrations"
367     _auto = False
368     _columns = {
369         'name': fields.char('Event', size=20), 
370         'date_begin': fields.datetime('Beginning date', required=True), 
371         'date_end': fields.datetime('Ending date', required=True), 
372         'draft_state': fields.integer('Draft Registration', size=20), 
373         'confirm_state': fields.integer('Confirm Registration', size=20), 
374         'register_max': fields.integer('Maximum Registrations'), 
375     }
376     def init(self, cr):
377         cr.execute("""
378             create or replace view report_event_registration as (
379                 select
380                 e.id as id,
381                 c.name as name,
382                 e.date_begin as date_begin,
383                 e.date_end as date_end,
384                 (SELECT sum(c.nb_register) FROM event_registration  c  WHERE c.section_id=e.section_id and state in ('draft')) as draft_state,
385                 (SELECT sum(c.nb_register) FROM event_registration  c  WHERE c.section_id=e.section_id and state in ('open')) as confirm_state,
386                 e.register_max as register_max
387                 from
388                 event_event e,crm_case_section c
389                 where
390                 e.section_id=c.id
391                 )""")
392
393 report_event_registration()
394
395 class report_event_type_registration(osv.osv):
396     _name = "report.event.type.registration"
397     _description = "Event type on registration"
398     _auto = False
399     _columns = {
400         'name': fields.char('Event Type', size=20), 
401         'nbevent': fields.integer('Number Of Events'), 
402         'draft_state': fields.integer('Draft Registrations', size=20), 
403         'confirm_state': fields.integer('Confirm Registrations', size=20), 
404         }
405         
406         
407     def init(self, cr):
408         cr.execute("""
409             create or replace view report_event_type_registration as (
410                 select
411                 count(t.id) as id,
412                 t.name as name,
413                 (select sum(c.nb_register) from event_registration c, event_event e  where c.section_id=e.section_id and c.state='draft' and e.type=t.id ) as draft_state ,
414                 (select sum(c.nb_register) from event_registration c, event_event e  where c.section_id=e.section_id and c.state='open' and e.type=t.id ) as  confirm_state,
415                 count(t.id) as nbevent
416                 from
417                     event_event e
418                 inner join
419                     crm_case_section c1 on (e.section_id=c1.id)
420                 inner join
421                     event_type t on (e.type=t.id)
422                 group by
423                     t.name,t.id
424
425             )""")
426 report_event_type_registration()
427
428
429 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
430