[IMP]: event: use inherit in place of inherits.Improve report using report guidlines.
[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 crm import crm
23 from osv import fields, osv
24 from tools.translate import _
25 import netsvc
26 import pooler
27 import time
28 import tools
29
30
31 class event_type(osv.osv):
32     
33     """ Event Type """
34     
35     _name = 'event.type'
36     _description = __doc__
37     _columns = {
38         'name': fields.char('Event type', size=64, required=True), 
39     }
40     
41 event_type()
42
43 class event(osv.osv):
44     
45     """Event"""
46     
47     _name = 'event.event'
48     _description = __doc__
49     _inherit = 'crm.case.section'
50     _order = 'date_begin'
51
52     def copy(self, cr, uid, id, default=None, context=None):
53         
54         """
55         Copy record of Given id
56         @param self: The object pointer
57         @param cr: the current row, from the database cursor,
58         @param uid: the current user’s ID for security checks,
59         @param id: Id of Event Registration type record.
60         @param context: A standard dictionary for contextual values
61         """
62         
63         return super(event, self).copy(cr, uid, id, default={'code': self.pool.get('ir.sequence').get(cr, uid, 'event.event'), 'state': 'draft'})
64
65     def button_draft(self, cr, uid, ids, context={}):
66         return self.write(cr, uid, ids, {'state': 'draft'})
67
68     def button_cancel(self, cr, uid, ids, context={}):
69         return self.write(cr, uid, ids, {'state': 'cancel'})
70
71     def button_done(self, cr, uid, ids, context={}):
72         return self.write(cr, uid, ids, {'state': 'done'})
73
74     def button_confirm(self, cr, uid, ids, context={}):
75         
76         for eve in self.browse(cr, uid, ids):
77             if eve.mail_auto_confirm:
78                 #send reminder that will confirm the event for all the people that were already confirmed
79                 reg_ids = self.pool.get('event.registration').search(cr, uid, 
80                                                                      [('event_id', '=', eve.id), 
81                                                                       ('state', 'not in', ['draft', 'cancel'])])
82                 if reg_ids:
83                     self.pool.get('event.registration').mail_user_confirm(cr, uid, reg_ids)
84                     
85         return self.write(cr, uid, ids, {'state': 'confirm'})
86
87     def _get_register(self, cr, uid, ids, name, args, context=None):
88         
89         """
90         Get Confirm or uncofirm register value. 
91         @param self: The object pointer
92         @param cr: the current row, from the database cursor,
93         @param uid: the current user’s ID for security checks,
94         @param ids: List of Event registration type's id
95         @param name: List of function fields(register_current and register_prospect).
96         @param context: A standard dictionary for contextual values
97         @return: Dictionary of function fields value. 
98         """
99         
100         res = {}
101         for event in self.browse(cr, uid, ids, context):
102             res[event.id] = {}
103             state = 'draft'
104             if name[0] == 'register_current':
105                 state = 'open'
106             query = """SELECT sum(r.nb_register) 
107                         from event_registration r 
108                         where state=%s and event_id=%s"""
109
110             cr.execute(query, (state, event.id,))
111             res2 = cr.fetchone()
112             
113             if res2 and res2[0]:
114                 res[event.id][name[0]] = res2[0]
115             else:
116                 res[event.id][name[0]] = 0
117                
118         return res
119
120     def write(self, cr, uid, ids, vals, *args, **kwargs):
121         """
122         Writes values in one or several fields.
123         @param cr: the current row, from the database cursor,
124         @param uid: the current user’s ID for security checks,
125         @param ids: List of Event registration type's IDs
126         @param vals: dictionary with values to update.
127         @return: True
128         """
129         
130         res = super(event, self).write(cr, uid, ids, vals, *args, **kwargs)
131         if 'date_begin' in vals and vals['date_begin']:
132             for eve in self.browse(cr, uid, ids):
133                 #change the deadlines of the registration linked to this event
134                 reg_ids = self.pool.get('event.registration').search(cr, uid, 
135                                                                      [('event_id', '=', eve.id)])
136                 if reg_ids:
137                     self.pool.get('event.registration').write(cr, uid, reg_ids, 
138                                                               {'date_deadline': vals['date_begin']})
139
140         #change the description of the registration linked to this event
141         if 'mail_auto_confirm' in vals:
142             if vals['mail_auto_confirm']:
143                 if 'mail_confirm' not in vals:
144                     for eve in self.browse(cr, uid, ids):
145                         vals['mail_confirm'] = eve.mail_confirm
146             else:
147                 vals['mail_confirm'] = False
148         if 'mail_confirm' in vals:
149             for eve in self.browse(cr, uid, ids):
150                 reg_ids = self.pool.get('event.registration').search(cr, uid, 
151                                                                      [('event_id', '=', eve.id)])
152                 if reg_ids:
153                     self.pool.get('event.registration').write(cr, uid, reg_ids, 
154                                                               {'description': vals['mail_confirm']})
155         return res
156
157     _columns = {
158         'type': fields.many2one('event.type', 'Type'), 
159         'register_max': fields.integer('Maximum Registrations'), 
160         'register_min': fields.integer('Minimum Registrations'), 
161         'register_current': fields.function(_get_register, method=True, string='Confirmed Registrations', multi='register_current'), 
162         'register_prospect': fields.function(_get_register, method=True, string='Unconfirmed Registrations', multi='register_prospect'), 
163         'date_begin': fields.datetime('Beginning date', required=True), 
164         'date_end': fields.datetime('Ending date', required=True), 
165         'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, required=True, 
166                                   help='If event is created, the state is \'Draft\'.\n If event is confirmed for the particular dates the state is set to \'Confirmed\'.\
167                                   \nIf the event is over, the state is set to \'Done\'.\n If event is cancelled the state is set to \'Cancelled\'.'), 
168         'mail_auto_registr': fields.boolean('Mail Auto Register', help='Check this box if you want to use the automatic mailing for new registration'), 
169         'mail_auto_confirm': fields.boolean('Mail Auto Confirm', help='Check this box if you want ot use the automatic confirmation emailing or the reminder'), 
170         'mail_registr': fields.text('Registration Email', help='This email will be sent when someone subscribes to the event.'), 
171         '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."), 
172         'product_id': fields.many2one('product.product', 'Product', required=True), 
173     }
174
175     _defaults = {
176         'state': lambda *args: 'draft', 
177         'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'event.event'), 
178         'user_id': lambda self, cr, uid, ctx: uid, 
179     }
180
181 event()
182
183 class event_registration(osv.osv):
184     
185     """Event Registration"""
186
187     def check_confirm(self, cr, uid, ids, context):
188         
189         """
190         Check confirm event register on given id.
191         @param cr: the current row, from the database cursor,
192         @param uid: the current user’s ID for security checks,
193         @param ids: List of Event registration's IDs
194         @param context: A standard dictionary for contextual values
195         @return: Dictionary value which open Confirm registration form.
196         """
197         
198         mod_obj = self.pool.get('ir.model.data')
199         current_registration = self.browse(cr, uid, [ids[0]])[0]
200         total_confirmed = current_registration.event_id.register_current + current_registration.nb_register
201         if total_confirmed <= current_registration.event_id.register_max or current_registration.event_id.register_max == 0:
202             self.write(cr, uid, [ids[0]], {'state': 'open'}, context=context)
203             self._history(cr, uid, [ids[0]], 'Open', history=True)
204             self.mail_user(cr, uid, [ids[0]])
205             return True
206         else:
207             model_data_ids = mod_obj.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_event_confirm_registration')], context=context)
208             resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
209             context.update({'reg_id': ids[0]})
210             return {
211                 'name': _('Confirm Registration'), 
212                 'context': context, 
213                 'view_type': 'form', 
214                 'view_mode': 'tree,form', 
215                 'res_model': 'event.confirm.registration', 
216                 'views': [(resource_id, 'form')], 
217                 'type': 'ir.actions.act_window', 
218                 'target': 'new', 
219                 'nodestroy': True
220             }
221
222     def _history(self, cr, uid, ids, keyword, history=False, email=False, context={}):
223         
224         """
225         Create value in mailgateway.message model.
226         @param cr: the current row, from the database cursor,
227         @param uid: the current user’s ID for security checks,
228         @param ids: List of Event registration's IDs
229         @param context: A standard dictionary for contextual values
230         @return: True
231         """
232         
233         for case in self.browse(cr, uid, ids):
234             data = {
235                 'name': keyword, 
236                 'som': case.som.id, 
237                 'canal_id': case.canal_id.id, 
238                 'user_id': uid, 
239             }
240             obj = self.pool.get('mailgate.message')
241             obj.create(cr, uid, data, context=context)
242         return True
243
244     def button_reg_close(self, cr, uid, ids, *args):
245         self.write(cr, uid, ids, {'state': 'done', })
246         self._history(cr, uid, ids, 'Done', history=True)
247         return True
248
249     def button_reg_cancel(self, cr, uid, ids, *args):
250         self.write(cr, uid, ids, {'state': 'cancel', })
251         self._history(cr, uid, ids, 'Cancel', history=True)
252         return True
253
254     def create(self, cr, uid, *args, **argv):
255         """ Overrides orm create method.
256         @param self: The object pointer
257         @param cr: the current row, from the database cursor,
258         @param uid: the current user’s ID for security checks,
259         @param *args: Fields value
260         @return : New created record Id.
261         """
262
263         event = self.pool.get('event.event').browse(cr, uid, args[0]['event_id'], None)
264         
265         args[0]['date_deadline']= event.date_begin
266         args[0]['description']= event.mail_confirm
267         res = super(event_registration, self).create(cr, uid, *args, **argv)
268         
269         self._history(cr, uid, [res], 'Created', history=True)
270         
271         return res
272
273     def write(self, cr, uid, *args, **argv):
274     
275         if 'event_id' in args[1]:
276             event = self.pool.get('event.event').browse(cr, uid, args[1]['event_id'], None)
277            
278             args[1]['date_deadline']= event.date_begin
279             args[1]['description']= event.mail_confirm
280         return super(event_registration, self).write(cr, uid, *args, **argv)
281
282     def mail_user_confirm(self, cr, uid, ids):
283         """
284         @param self: The object pointer
285         @param cr: the current row, from the database cursor,
286         @param uid: the current user’s ID for security checks,
287         @param ids: List of Event Registration's Id.
288         @return : False
289         """
290        
291         reg_ids = self.browse(cr, uid, ids)
292         for reg_id in reg_ids:
293             src = reg_id.event_id.reply_to or False
294             dest = []
295             if reg_id.email_from:
296                 dest += [reg_id.email_from]
297             if reg_id.email_cc:
298                 dest += [reg_id.email_cc]
299             if dest and src:
300                 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))
301             if not src:
302                 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'))
303         return False
304
305     def mail_user(self, cr, uid, ids):
306         """
307         @param self: The object pointer
308         @param cr: the current row, from the database cursor,
309         @param uid: the current user’s ID for security checks,
310         @param ids: List of Event Registration's Id.
311         @return : False
312         """
313         reg_ids = self.browse(cr, uid, ids)
314         
315         for reg_id in reg_ids:
316             src = reg_id.event_id.reply_to or False
317             dest = []
318             if reg_id.email_from:
319                 dest += [reg_id.email_from]
320             if reg_id.email_cc:
321                 dest += [reg_id.email_cc]
322             if reg_id.event_id.mail_auto_confirm or reg_id.event_id.mail_auto_registr:
323                 if dest and src:
324                     if reg_id.event_id.state in ['draft', 'fixed', 'open', 'confirm', 'running'] and reg_id.event_id.mail_auto_registr:
325                         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))
326                     if (reg_id.event_id.state in ['confirm', 'running']) and reg_id.event_id.mail_auto_confirm:
327                         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))
328                 if not src:
329                     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'))
330         return False
331
332     def _create_invoice_lines(self, cr, uid, ids, vals):
333         
334         """ Create account Invoice line for Registration Id.
335         @param self: The object pointer
336         @param cr: the current row, from the database cursor,
337         @param uid: the current user’s ID for security checks,
338         @param Ids: List of event registration's Id
339         @param vals: Create fields value
340         @return : New created record Id.
341         """
342         
343         return self.pool.get('account.invoice.line').create(cr, uid, vals)
344
345     _name= 'event.registration'
346     _description = __doc__
347     _inherit = 'crm.meeting'
348
349     _columns = {
350         'email_cc': fields.text('Watchers Emails', size=252 , help="These \
351 people will receive a copy of the future communication between partner \
352 and users by email"), 
353         'nb_register': fields.integer('Number of Registration', readonly=True, states={'draft': [('readonly', False)]}), 
354         'event_id': fields.many2one('event.event', 'Event Related', required=True), 
355         "partner_invoice_id": fields.many2one('res.partner', 'Partner Invoiced'), 
356         "contact_id": fields.many2one('res.partner.contact', 'Partner Contact'), #TODO: filter only the contacts that have a function into the selected partner_id
357         "unit_price": fields.float('Unit Price'), 
358         "badge_title": fields.char('Badge Title', size=128), 
359         "badge_name": fields.char('Badge Name', size=128), 
360         "badge_partner": fields.char('Badge Partner', size=128), 
361         "invoice_label": fields.char("Label Invoice", size=128, required=True), 
362         "tobe_invoiced": fields.boolean("To be Invoiced"), 
363         "invoice_id": fields.many2one("account.invoice", "Invoice"), 
364         'date_closed': fields.datetime('Closed', readonly=True), 
365         'ref': fields.reference('Reference', selection=crm._links_get, size=128), 
366         'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128), 
367         'canal_id': fields.many2one('res.partner.canal', 'Channel', help="The channels represent the different communication modes available with the customer." \
368                                                                         " With each commercial opportunity, you can indicate the canall which is this opportunity source."), 
369         'som': fields.many2one('res.partner.som', 'State of Mind', help="The minds states allow to define a value scale which represents" \
370                                                                        "the partner mentality in relation to our services.The scale has" \
371                                                                        "to be created with a factor for each level from 0 (Very dissatisfied) to 10 (Extremely satisfied)."), 
372     }
373     
374     _defaults = {
375         'nb_register': lambda *a: 1, 
376         'tobe_invoiced': lambda *a: True, 
377         'name': lambda *a: 'Registration', 
378     }
379
380     def onchange_badge_name(self, cr, uid, ids, badge_name):
381         
382         data ={}
383         if not badge_name:
384             return data
385         data['name'] = 'Registration: ' + badge_name
386         return {'value': data}
387
388     def onchange_contact_id(self, cr, uid, ids, contact, partner):
389         
390         data ={}
391         if not contact:
392             return data
393         
394         contact_id = self.pool.get('res.partner.contact').browse(cr, uid, contact)
395         data['badge_name'] = contact_id.name
396         data['badge_title'] = contact_id.title
397         if partner:
398             partner_addresses = self.pool.get('res.partner.address').search(cr, uid, [('partner_id', '=', partner)])
399             job_ids = self.pool.get('res.partner.job').search(cr, uid, [('contact_id', '=', contact), ('address_id', 'in', partner_addresses)])
400             if job_ids:
401                 data['email_from'] = self.pool.get('res.partner.job').browse(cr, uid, job_ids[0]).email
402         d = self.onchange_badge_name(cr, uid, ids, data['badge_name'])
403         data.update(d['value'])
404
405         return {'value': data}
406
407     def onchange_event(self, cr, uid, ids, event_id, partner_invoice_id):
408         context={}
409         if not event_id:
410             return {'value': {'unit_price': False, 'invoice_label': False}}
411         data_event =  self.pool.get('event.event').browse(cr, uid, event_id)
412         
413         if data_event.product_id:
414             if not partner_invoice_id:
415                 unit_price=self.pool.get('product.product').price_get(cr, uid, [data_event.product_id.id], context=context)[data_event.product_id.id]
416                 return {'value': {'unit_price': unit_price, 'invoice_label': data_event.product_id.name}}
417             data_partner = self.pool.get('res.partner').browse(cr, uid, partner_invoice_id)
418             context.update({'partner_id': data_partner})
419             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]
420             return {'value': {'unit_price': unit_price, 'invoice_label': data_event.product_id.name}}
421         
422         return {'value': {'unit_price': False, 'invoice_label': False}}
423
424
425     def onchange_partner_id(self, cr, uid, ids, part, event_id, email=False):
426         
427         data={}
428         data['badge_partner'] = data['contact_id'] = data['partner_invoice_id'] = data['email_from'] = data['badge_title'] = data['badge_name'] = False
429         if not part:
430             return {'value': data}
431         data['partner_invoice_id']=part
432         # this calls onchange_partner_invoice_id
433         d = self.onchange_partner_invoice_id(cr, uid, ids, event_id, part)
434         # this updates the dictionary
435         data.update(d['value'])
436         addr = self.pool.get('res.partner').address_get(cr, uid, [part])
437         if addr:
438             if addr.has_key('default'):
439                 job_ids = self.pool.get('res.partner.job').search(cr, uid, [('address_id', '=', addr['default'])])
440                 if job_ids:
441                     data['contact_id'] = self.pool.get('res.partner.job').browse(cr, uid, job_ids[0]).contact_id.id
442                     d = self.onchange_contact_id(cr, uid, ids, data['contact_id'], part)
443                     data.update(d['value'])
444         partner_data = self.pool.get('res.partner').browse(cr, uid, part)
445         data['badge_partner'] = partner_data.name
446         return {'value': data}
447
448     def onchange_partner_invoice_id(self, cr, uid, ids, event_id, partner_invoice_id):
449         
450         data={}
451         context={}
452         data['unit_price']=False
453         if not event_id:
454             return {'value': data}
455         data_event =  self.pool.get('event.event').browse(cr, uid, event_id)
456
457         if data_event.product_id:
458             if not partner_invoice_id:
459                 data['unit_price']=self.pool.get('product.product').price_get(cr, uid, [data_event.product_id.id], context=context)[data_event.product_id.id]
460                 return {'value': data}
461             data_partner = self.pool.get('res.partner').browse(cr, uid, partner_invoice_id)
462             context.update({'partner_id': data_partner})
463             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]
464             return {'value': data}
465         return {'value': data}
466
467
468 event_registration()
469
470
471 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
472