[IMP] improves the add/remove actions and preview buttons in email template form...
[odoo/odoo.git] / addons / event / event.py
index 0cff38f..497d75f 100644 (file)
@@ -20,8 +20,8 @@
 ##############################################################################
 
 from datetime import datetime, timedelta
-from osv import fields, osv
-from tools.translate import _
+from openerp.osv import fields, osv
+from openerp.tools.translate import _
 from openerp import SUPERUSER_ID
 
 class event_type(osv.osv):
@@ -41,14 +41,12 @@ class event_type(osv.osv):
         'default_registration_max': 0,
     }
 
-event_type()
-
 class event_event(osv.osv):
     """Event"""
     _name = 'event.event'
     _description = __doc__
     _order = 'date_begin'
-    _inherit = ['mail.thread','ir.needaction_mixin']
+    _inherit = ['mail.thread', 'ir.needaction_mixin']
 
     def name_get(self, cr, uid, ids, context=None):
         if not ids:
@@ -67,11 +65,6 @@ class event_event(osv.osv):
             res.append((record['id'], display_name))
         return res
 
-    def create(self, cr, uid, vals, context=None):
-        obj_id = super(event_event, self).create(cr, uid, vals, context)
-        self.create_send_note(cr, uid, [obj_id], context=context)
-        return obj_id
-
     def copy(self, cr, uid, id, default=None, context=None):
         """ Reset the state and the registrations while copying an event
         """
@@ -84,7 +77,6 @@ class event_event(osv.osv):
         return super(event_event, self).copy(cr, uid, id, default=default, context=context)
 
     def button_draft(self, cr, uid, ids, context=None):
-        self.button_draft_send_note(cr, uid, ids, context=context)
         return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
 
     def button_cancel(self, cr, uid, ids, context=None):
@@ -94,79 +86,55 @@ class event_event(osv.osv):
             if event_reg.state == 'done':
                 raise osv.except_osv(_('Error!'),_("You have already set a registration for this event as 'Attended'. Please reset it to draft if you want to cancel this event.") )
         registration.write(cr, uid, reg_ids, {'state': 'cancel'}, context=context)
-        self.button_cancel_send_note(cr, uid, ids, context=context)
         return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
 
     def button_done(self, cr, uid, ids, context=None):
-        self.button_done_send_note(cr, uid, ids, context=context)
         return self.write(cr, uid, ids, {'state': 'done'}, context=context)
 
-    def check_registration_limits(self, cr, uid, ids, context=None):
-        for self.event in self.browse(cr, uid, ids, context=context):
-            total_confirmed = self.event.register_current
-            if total_confirmed < self.event.register_min or total_confirmed > self.event.register_max and self.event.register_max!=0:
-                raise osv.except_osv(_('Error!'),_("The total of confirmed registration for the event '%s' does not meet the expected minimum/maximum. Please reconsider those limits before going further.") % (self.event.name))
-
-    def check_registration_limits_before(self, cr, uid, ids, no_of_registration, context=None):
-        for event in self.browse(cr, uid, ids, context=context):
-            available_seats = event.register_avail
-            if available_seats and no_of_registration > available_seats:
-                raise osv.except_osv(_('Warning!'),_("Only %d Seats are Available!") % (available_seats))
-            elif available_seats == 0:
-                raise osv.except_osv(_('Warning!'),_("No Tickets Available!"))
-
     def confirm_event(self, cr, uid, ids, context=None):
         register_pool = self.pool.get('event.registration')
-        if self.event.email_confirmation_id:
-        #send reminder that will confirm the event for all the people that were already confirmed
-            reg_ids = register_pool.search(cr, uid, [
-                               ('event_id', '=', self.event.id),
-                               ('state', 'not in', ['draft', 'cancel'])], context=context)
-            register_pool.mail_user_confirm(cr, uid, reg_ids)
+        for event in self.browse(cr, uid, ids, context=context):
+            if event.email_confirmation_id:
+            #send reminder that will confirm the event for all the people that were already confirmed
+                reg_ids = register_pool.search(cr, uid, [
+                                   ('event_id', '=', event.id),
+                                   ('state', 'not in', ['draft', 'cancel'])], context=context)
+                register_pool.mail_user_confirm(cr, uid, reg_ids)
         return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
 
     def button_confirm(self, cr, uid, ids, context=None):
         """ Confirm Event and send confirmation email to all register peoples
         """
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        self.check_registration_limits(cr, uid, ids, context=context)
-        self.button_confirm_send_note(cr, uid, ids, context=context)
-        return self.confirm_event(cr, uid, ids, context=context)
-
-    def _get_register(self, cr, uid, ids, fields, args, context=None):
-        """Get Confirm or uncofirm register value.
-        @param ids: List of Event registration type's id
-        @param fields: List of function fields(register_current and register_prospect).
-        @param context: A standard dictionary for contextual values
-        @return: Dictionary of function fields value.
+        return self.confirm_event(cr, uid, isinstance(ids, (int, long)) and [ids] or ids, context=context)
+
+    def _get_seats(self, cr, uid, ids, fields, args, context=None):
+        """Get reserved, available, reserved but unconfirmed and used seats.
+        @return: Dictionary of function field values.
         """
+        keys = {'draft': 'seats_unconfirmed', 'open':'seats_reserved', 'done': 'seats_used'}
         res = {}
-        for event in self.browse(cr, uid, ids, context=context):
-            res[event.id] = {}
-            reg_open = reg_done = reg_draft =0
-            for registration in event.registration_ids:
-                if registration.state == 'open':
-                    reg_open += registration.nb_register
-                elif registration.state == 'done':
-                    reg_done += registration.nb_register
-                elif registration.state == 'draft':
-                    reg_draft += registration.nb_register
-            for field in fields:
-                number = 0
-                if field == 'register_current':
-                    number = reg_open
-                elif field == 'register_attended':
-                    number = reg_done
-                elif field == 'register_prospect':
-                    number = reg_draft
-                elif field == 'register_avail':
-                    #the number of ticket is unlimited if the event.register_max field is not set.
-                    #In that cas we arbitrary set it to 9999, it is used in the kanban view to special case the display of the 'subscribe' button
-                    number = event.register_max - reg_open if event.register_max != 0 else 9999
-                res[event.id][field] = number
+        for event_id in ids:
+            res[event_id] = {key:0 for key in keys.values()}
+        query = "SELECT state, sum(nb_register) FROM event_registration WHERE event_id = %s AND state IN ('draft','open','done') GROUP BY state"
+        for event in self.pool.get('event.event').browse(cr, uid, ids, context=context):
+            cr.execute(query, (event.id,))
+            reg_states = cr.fetchall()
+            for reg_state in reg_states:
+                res[event.id][keys[reg_state[0]]] = reg_state[1]
+            res[event.id]['seats_available'] = event.seats_max - \
+                (res[event.id]['seats_reserved'] + res[event.id]['seats_used']) \
+                if event.seats_max > 0 else None
         return res
 
+    def _get_events_from_registrations(self, cr, uid, ids, context=None):
+        """Get reserved, available, reserved but unconfirmed and used seats, of the event related to a registration.
+        @return: Dictionary of function field values.
+        """
+        event_ids=set()
+        for registration in self.browse(cr, uid, ids, context=context):
+            event_ids.add(registration.event_id.id)
+        return list(event_ids)
+
     def _subscribe_fnc(self, cr, uid, ids, fields, args, context=None):
         """This functional fields compute if the current user (uid) is already subscribed or not to the event passed in parameter (ids)
         """
@@ -181,18 +149,38 @@ class event_event(osv.osv):
                         res[event.id]= True
                         continue
         return res
+    
+    def _count_all(self, cr, uid, ids, field_name, arg, context=None):
+        res = dict(map(lambda x: (x,{'count_regitrations': 0, 'count_tracks': 0,}), ids))
+        try:
+            for data in self.browse(cr, uid, ids, context=context):
+                res[data.id] = {'count_regitrations': len(data.registration_ids),
+                'count_tracks': len(data.track_ids),
+               }
+        except:
+            pass
+        return res
 
     _columns = {
-        'name': fields.char('Name', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
+        'name': fields.char('Event Name', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
         'user_id': fields.many2one('res.users', 'Responsible User', readonly=False, states={'done': [('readonly', True)]}),
         'type': fields.many2one('event.type', 'Type of Event', readonly=False, states={'done': [('readonly', True)]}),
-        'register_max': fields.integer('Maximum Registrations', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
-        'register_min': fields.integer('Minimum Registrations', help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
-        'register_current': fields.function(_get_register, string='Confirmed Registrations', multi='register_numbers'),
-        'register_avail': fields.function(_get_register, string='Available Registrations', multi='register_numbers',type='integer'),
-        'register_prospect': fields.function(_get_register, string='Unconfirmed Registrations', multi='register_numbers'),
-        'register_attended': fields.function(_get_register, string='# of Participations', multi='register_numbers'),
+        'seats_max': fields.integer('Maximum Avalaible Seats', oldname='register_max', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
+        'seats_min': fields.integer('Minimum Reserved Seats', oldname='register_min', help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
+        'seats_reserved': fields.function(_get_seats, oldname='register_current', string='Reserved Seats', type='integer', multi='seats_reserved',
+            store={'event.registration': (_get_events_from_registrations, ['state'], 10),
+                   'event.event': (lambda  self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
+        'seats_available': fields.function(_get_seats, oldname='register_avail', string='Available Seats', type='integer', multi='seats_reserved',
+            store={'event.registration': (_get_events_from_registrations, ['state'], 10),
+                   'event.event': (lambda  self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
+        'seats_unconfirmed': fields.function(_get_seats, oldname='register_prospect', string='Unconfirmed Seat Reservations', type='integer', multi='seats_reserved',
+            store={'event.registration': (_get_events_from_registrations, ['state'], 10),
+                   'event.event': (lambda  self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
+        'seats_used': fields.function(_get_seats, oldname='register_attended', string='Number of Participations', type='integer', multi='seats_reserved',
+            store={'event.registration': (_get_events_from_registrations, ['state'], 10),
+                   'event.event': (lambda  self, cr, uid, ids, c = {}: ids, ['seats_max', 'registration_ids'], 20)}),
         'registration_ids': fields.one2many('event.registration', 'event_id', 'Registrations', readonly=False, states={'done': [('readonly', True)]}),
+        'track_ids': fields.one2many('event.track', 'event_id', 'Tracks', readonly=False),
         'date_begin': fields.datetime('Start Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
         'date_end': fields.datetime('End Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
         'state': fields.selection([
@@ -205,31 +193,41 @@ class event_event(osv.osv):
         'email_registration_id' : fields.many2one('email.template','Registration Confirmation Email', help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.'),
         'email_confirmation_id' : fields.many2one('email.template','Event Confirmation Email', help="If you set an email template, each participant will receive this email announcing the confirmation of the event."),
         'reply_to': fields.char('Reply-To Email', size=64, readonly=False, states={'done': [('readonly', True)]}, help="The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one."),
-        'main_speaker_id': fields.many2one('res.partner','Main Speaker', readonly=False, states={'done': [('readonly', True)]}, help="Speaker who will be giving speech at the event."),
-        'address_id': fields.many2one('res.partner','Location Address', readonly=False, states={'done': [('readonly', True)]}),
-        'street': fields.related('address_id','street',type='char',string='Street'),
-        'street2': fields.related('address_id','street2',type='char',string='Street2'),
-        'state_id': fields.related('address_id','state_id',type='many2one', relation="res.country.state", string='State'),
-        'zip': fields.related('address_id','zip',type='char',string='zip'),
-        'city': fields.related('address_id','city',type='char',string='city'),
-        'speaker_confirmed': fields.boolean('Speaker Confirmed', readonly=False, states={'done': [('readonly', True)]}),
+        'address_id': fields.many2one('res.partner','Location', readonly=False, states={'done': [('readonly', True)]}),
         'country_id': fields.related('address_id', 'country_id',
-                    type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}),
-        'note': fields.text('Description', readonly=False, states={'done': [('readonly', True)]}),
+                    type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}, store=True),
+        'description': fields.html(
+            'Description', readonly=False,
+            states={'done': [('readonly', True)]},
+            oldname='note'),
         'company_id': fields.many2one('res.company', 'Company', required=False, change_default=True, readonly=False, states={'done': [('readonly', True)]}),
         'is_subscribed' : fields.function(_subscribe_fnc, type="boolean", string='Subscribed'),
+        'organizer_id': fields.many2one('res.partner', "Organizer"),
+        'count_regitrations': fields.function(_count_all, type="integer", string="Registrations", multi=True),
+        'count_tracks': fields.function(_count_all, type='integer', string='Tracks', multi=True),
     }
     _defaults = {
         'state': 'draft',
         'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'event.event', context=c),
         'user_id': lambda obj, cr, uid, context: uid,
+        'organizer_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id,
+        'address_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id
     }
 
+    def _check_seats_limit(self, cr, uid, ids, context=None):
+        for event in self.browse(cr, uid, ids, context=context):
+            if event.seats_max and event.seats_available < 0:
+                return False
+        return True
+
+    _constraints = [
+        (_check_seats_limit, 'No more available seats.', ['registration_ids','seats_max']),
+    ]
+
     def subscribe_to_event(self, cr, uid, ids, context=None):
         register_pool = self.pool.get('event.registration')
         user_pool = self.pool.get('res.users')
         num_of_seats = int(context.get('ticket', 1))
-        self.check_registration_limits_before(cr, uid, ids, num_of_seats, context=context)
         user = user_pool.browse(cr, uid, uid, context=context)
         curr_reg_ids = register_pool.search(cr, uid, [('user_id', '=', user.id), ('event_id', '=' , ids[0])])
         #the subscription is done with SUPERUSER_ID because in case we share the kanban view, we want anyone to be able to subscribe
@@ -256,31 +254,18 @@ class event_event(osv.osv):
     ]
 
     def onchange_event_type(self, cr, uid, ids, type_event, context=None):
+        values = {}
         if type_event:
             type_info =  self.pool.get('event.type').browse(cr,uid,type_event,context)
             dic ={
               'reply_to': type_info.default_reply_to,
               'email_registration_id': type_info.default_email_registration.id,
               'email_confirmation_id': type_info.default_email_event.id,
-              'register_min': type_info.default_registration_min,
-              'register_max': type_info.default_registration_max,
+              'seats_min': type_info.default_registration_min,
+              'seats_max': type_info.default_registration_max,
             }
-            return {'value': dic}
-
-    def on_change_address_id(self, cr, uid, ids, address_id, context=None):
-        values = {}
-        if not address_id:
-            return values
-        address = self.pool.get('res.partner').browse(cr, uid, address_id, context=context)
-        values.update({
-            'street' : address.street,
-            'street2' : address.street2,
-            'city' : address.city,
-            'country_id' : address.country_id and address.country_id.id or False,
-            'state_id' : address.state_id and address.state_id.id or False,
-            'zip' : address.zip,
-        })
-        return {'value' : values}
+            values.update(dic)
+        return values
 
     def onchange_start_date(self, cr, uid, ids, date_begin=False, date_end=False, context=None):
         res = {'value':{}}
@@ -292,45 +277,15 @@ class event_event(osv.osv):
             res['value'] = {'date_end': date_end.strftime("%Y-%m-%d %H:%M:%S")}
         return res
 
-    # ----------------------------------------
-    # OpenChatter methods and notifications
-    # ----------------------------------------
-
-    def create_send_note(self, cr, uid, ids, context=None):
-        message = _("Event has been <b>created</b>.")
-        self.message_post(cr, uid, ids, body=message, context=context)
-        return True
-
-    def button_cancel_send_note(self, cr, uid, ids, context=None):
-        message = _("Event has been <b>cancelled</b>.")
-        self.message_post(cr, uid, ids, body=message, context=context)
-        return True
-
-    def button_draft_send_note(self, cr, uid, ids, context=None):
-        message = _("Event has been set to <b>draft</b>.")
-        self.message_post(cr, uid, ids, body=message, context=context)
-        return True
-
-    def button_done_send_note(self, cr, uid, ids, context=None):
-        message = _("Event has been <b>done</b>.")
-        self.message_post(cr, uid, ids, body=message, context=context)
-        return True
-
-    def button_confirm_send_note(self, cr, uid, ids, context=None):
-        message = _("Event has been <b>confirmed</b>.")
-        self.message_post(cr, uid, ids, body=message, context=context)
-        return True
-
-event_event()
 
 class event_registration(osv.osv):
     """Event Registration"""
     _name= 'event.registration'
     _description = __doc__
-    _inherit = ['ir.needaction_mixin','mail.thread']
+    _inherit = ['mail.thread', 'ir.needaction_mixin']
     _columns = {
         'id': fields.integer('ID'),
-        'origin': fields.char('Source Document', size=124,readonly=True,help="Name of the sale order which create the registration"),
+        'origin': fields.char('Source Document', size=124,readonly=True,help="Reference of the sales order which created the registration"),
         'nb_register': fields.integer('Number of Participants', required=True, readonly=True, states={'draft': [('readonly', False)]}),
         'event_id': fields.many2one('event.event', 'Event', required=True, readonly=True, states={'draft': [('readonly', False)]}),
         'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}),
@@ -338,7 +293,7 @@ class event_registration(osv.osv):
         'date_closed': fields.datetime('Attended Date', readonly=True),
         'date_open': fields.datetime('Registration Date', readonly=True),
         'reply_to': fields.related('event_id','reply_to',string='Reply-to Email', type='char', size=128, readonly=True,),
-        'log_ids': fields.one2many('mail.message', 'res_id', 'Logs', domain=[('email_from', '=', False),('model','=',_name)]),
+        'log_ids': fields.one2many('mail.message', 'res_id', 'Logs', domain=[('model','=',_name)]),
         'event_end_date': fields.related('event_id','date_end', type='datetime', string="Event End Date", readonly=True),
         'event_begin_date': fields.related('event_id', 'date_begin', type='datetime', string="Event Start Date", readonly=True),
         'user_id': fields.many2one('res.users', 'User', states={'done': [('readonly', True)]}),
@@ -358,29 +313,29 @@ class event_registration(osv.osv):
     }
     _order = 'name, create_date desc'
 
+
+    def _check_seats_limit(self, cr, uid, ids, context=None):
+        for registration in self.browse(cr, uid, ids, context=context):
+            if registration.event_id.seats_max and \
+                registration.event_id.seats_available < (registration.state == 'draft' and registration.nb_register or 0):
+                return False
+        return True
+
+    _constraints = [
+        (_check_seats_limit, 'No more available seats.', ['event_id','nb_register','state']),
+    ]
+
     def do_draft(self, cr, uid, ids, context=None):
-        self.do_draft_send_note(cr, uid, ids, context=context)
         return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
 
     def confirm_registration(self, cr, uid, ids, context=None):
         for reg in self.browse(cr, uid, ids, context=context or {}):
             self.pool.get('event.event').message_post(cr, uid, [reg.event_id.id], body=_('New registration confirmed: %s.') % (reg.name or '', ),subtype="event.mt_event_registration", context=context)
-        self.message_post(cr, uid, ids, body=_('Registration confirmed.'), context=context)
-        return self.write(cr, uid, ids, {'state': 'open'},context=context)
-
-    def create(self, cr, uid, vals, context=None):
-        obj_id = super(event_registration, self).create(cr, uid, vals, context)
-        self.create_send_note(cr, uid, [obj_id], context=context)
-        return obj_id
+        return self.write(cr, uid, ids, {'state': 'open'}, context=context)
 
     def registration_open(self, cr, uid, ids, context=None):
         """ Open Registration
         """
-        event_obj = self.pool.get('event.event')
-        for register in  self.browse(cr, uid, ids, context=context):
-            event_id = register.event_id.id
-            no_of_registration = register.nb_register
-            event_obj.check_registration_limits_before(cr, uid, [event_id], no_of_registration, context=context)
         res = self.confirm_registration(cr, uid, ids, context=context)
         self.mail_user(cr, uid, ids, context=context)
         return res
@@ -395,13 +350,11 @@ class event_registration(osv.osv):
             if today >= registration.event_id.date_begin:
                 values = {'state': 'done', 'date_closed': today}
                 self.write(cr, uid, ids, values)
-                self.message_post(cr, uid, ids, body=_('State set to Done'), context=context)
             else:
                 raise osv.except_osv(_('Error!'), _("You must wait for the starting day of the event to do this action."))
         return True
 
     def button_reg_cancel(self, cr, uid, ids, context=None, *args):
-        self.message_post(cr, uid, ids, body=_('State set to Cancel'), context=context)
         return self.write(cr, uid, ids, {'state': 'cancel'})
 
     def mail_user(self, cr, uid, ids, context=None):
@@ -449,20 +402,4 @@ class event_registration(osv.osv):
             data.update(d['value'])
         return {'value': data}
 
-    # ----------------------------------------
-    # OpenChatter methods and notifications
-    # ----------------------------------------
-
-    def create_send_note(self, cr, uid, ids, context=None):
-        message = _("Registration has been <b>created</b>.")
-        self.message_post(cr, uid, ids, body=message, context=context)
-        return True
-
-    def do_draft_send_note(self, cr, uid, ids, context=None):
-        message = _("Registration has been set as <b>draft</b>.")
-        self.message_post(cr, uid, ids, body=message, context=context)
-        return True
-
-event_registration()
-
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: