b9db63e125cfb216c47ab55fa0ecd0694b499590
[odoo/odoo.git] / addons / event_sale / event_sale.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 openerp.osv import fields, osv
23 from openerp.tools.translate import _
24
25 class product_template(osv.osv):
26     _inherit = 'product.template'
27     _columns = {
28         'event_ok': fields.boolean('Event Subscription', help='Determine if a product needs to create automatically an event registration at the confirmation of a sales order line.'),
29         'event_type_id': fields.many2one('event.type', 'Type of Event', help='Select event types so when we use this product in sales order lines, it will filter events of this type only.'),
30     }
31
32     def onchange_event_ok(self, cr, uid, ids, type, event_ok, context=None):
33         if event_ok:
34             return {'value': {'type': 'service'}}
35         return {}
36
37 class product(osv.osv):
38     _inherit = 'product.product'
39
40     def onchange_event_ok(self, cr, uid, ids, type, event_ok, context=None):
41         # cannot directly forward to product.template as the ids are theoretically different
42         if event_ok:
43             return {'value': {'type': 'service'}}
44         return {}
45
46
47 class sale_order_line(osv.osv):
48     _inherit = 'sale.order.line'
49     _columns = {
50         'event_id': fields.many2one('event.event', 'Event',
51             help="Choose an event and it will automatically create a registration for this event."),
52         'event_ticket_id': fields.many2one('event.event.ticket', 'Event Ticket',
53             help="Choose an event ticket and it will automatically create a registration for this event ticket."),
54         #those 2 fields are used for dynamic domains and filled by onchange
55         'event_type_id': fields.related('product_id','event_type_id', type='many2one', relation="event.type", string="Event Type"),
56         'event_ok': fields.related('product_id', 'event_ok', string='event_ok', type='boolean'),
57     }
58
59     def product_id_change(self, cr, uid, ids,
60                           pricelist, 
61                           product,
62                           qty=0,
63                           uom=False,
64                           qty_uos=0,
65                           uos=False,
66                           name='',
67                           partner_id=False,
68                           lang=False,
69                           update_tax=True,
70                           date_order=False,
71                           packaging=False,
72                           fiscal_position=False,
73                           flag=False, context=None):
74         """
75         check product if event type
76         """
77         res = super(sale_order_line,self).product_id_change(cr, uid, ids, pricelist, product, qty=qty, uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id, lang=lang, update_tax=update_tax, date_order=date_order, packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context)
78         if product:
79             product_res = self.pool.get('product.product').browse(cr, uid, product, context=context)
80             if product_res.event_ok:
81                 res['value'].update({'event_type_id': product_res.event_type_id.id, 'event_ok':product_res.event_ok})
82         return res
83
84     def button_confirm(self, cr, uid, ids, context=None):
85         '''
86         create registration with sales order
87         '''
88         if context is None:
89             context = {}
90         registration_obj = self.pool.get('event.registration')
91         for order_line in self.browse(cr, uid, ids, context=context):
92             if order_line.event_id:
93                 dic = {
94                     'name': order_line.order_id.partner_invoice_id.name,
95                     'partner_id': order_line.order_id.partner_id.id,
96                     'nb_register': int(order_line.product_uom_qty),
97                     'email': order_line.order_id.partner_id.email,
98                     'phone': order_line.order_id.partner_id.phone,
99                     'origin': order_line.order_id.name,
100                     'event_id': order_line.event_id.id,
101                     'event_ticket_id': order_line.event_ticket_id and order_line.event_ticket_id.id or None,
102                 }
103
104                 if order_line.event_ticket_id:
105                     message = _("The registration has been created for event <i>%s</i> with the ticket <i>%s</i> from the Sale Order %s. ") % (order_line.event_id.name, order_line.event_ticket_id.name, order_line.order_id.name)
106                 else:
107                     message = _("The registration has been created for event <i>%s</i> from the Sale Order %s. ") % (order_line.event_id.name, order_line.order_id.name)
108                 
109                 context.update({'mail_create_nolog': True})
110                 registration_id = registration_obj.create(cr, uid, dic, context=context)
111                 registration_obj.message_post(cr, uid, [registration_id], body=message, context=context)
112         return super(sale_order_line, self).button_confirm(cr, uid, ids, context=context)
113
114     def onchange_event_ticket_id(self, cr, uid, ids, event_ticket_id=False, context=None):
115         price = event_ticket_id and self.pool.get("event.event.ticket").browse(cr, uid, event_ticket_id, context=context).price or False
116         return {'value': {'price_unit': price}}
117
118
119 class event_event(osv.osv):
120     _inherit = 'event.event'
121
122     def _get_seats_max(self, cr, uid, ids, field_name, arg, context=None):
123         result = dict.fromkeys(ids, 0)
124         for rec in self.browse(cr, uid, ids, context=context):
125             result[rec.id] = sum([ticket.seats_max for ticket in rec.event_ticket_ids])
126         return result
127
128     def _get_tickets(self, cr, uid, context={}):
129         try:
130             product = self.pool.get('ir.model.data').get_object(cr, uid, 'event_sale', 'product_product_event')
131             return [{
132                 'name': _('Subscription'),
133                 'product_id': product.id,
134                 'price': 0,
135             }]
136         except ValueError:
137             pass
138         return []
139
140     _columns = {
141         'event_ticket_ids': fields.one2many('event.event.ticket', "event_id", "Event Ticket"),
142         'seats_max': fields.function(_get_seats_max,
143             string='Maximum Avalaible Seats',
144             help="The maximum registration level is equal to the sum of the maximum registration of event ticket." +
145             "If you have too much registrations you are not able to confirm your event. (0 to ignore this rule )",
146             type='integer',
147             readonly=True)
148     }
149     _defaults = {
150         'event_ticket_ids': _get_tickets
151     }
152
153 class event_ticket(osv.osv):
154     _name = 'event.event.ticket'
155
156     def _get_seats(self, cr, uid, ids, fields, args, context=None):
157         """Get reserved, available, reserved but unconfirmed and used seats for each event tickets.
158         @return: Dictionary of function field values.
159         """
160         res = dict([(id, {}) for id in ids])
161         for ticket in self.browse(cr, uid, ids, context=context):
162             res[ticket.id]['seats_reserved'] = sum(reg.nb_register for reg in ticket.registration_ids if reg.state == "open")
163             res[ticket.id]['seats_used'] = sum(reg.nb_register for reg in ticket.registration_ids if reg.state == "done")
164             res[ticket.id]['seats_unconfirmed'] = sum(reg.nb_register for reg in ticket.registration_ids if reg.state == "draft")
165             res[ticket.id]['seats_available'] = ticket.seats_max - \
166                 (res[ticket.id]['seats_reserved'] + res[ticket.id]['seats_used']) \
167                 if ticket.seats_max > 0 else None
168         return res
169
170     def _is_expired(self, cr, uid, ids, field_name, args, context=None):
171         # FIXME: A ticket is considered expired when the deadline is passed. The deadline should
172         #        be considered in the timezone of the event, not the timezone of the user!
173         #        Until we add a TZ on the event we'll use the context's current date, more accurate
174         #        than using UTC all the time.
175         current_date = fields.date.context_today(self, cr, uid, context=context)
176         return {ticket.id: ticket.deadline and ticket.deadline < current_date
177                       for ticket in self.browse(cr, uid, ids, context=context)}
178         
179
180     _columns = {
181         'name': fields.char('Name', size=64, required=True),
182         'event_id': fields.many2one('event.event', "Event", required=True, ondelete='cascade'),
183         'product_id': fields.many2one('product.product', 'Product', required=True, domain=[("event_type_id", "!=", False)]),
184         'registration_ids': fields.one2many('event.registration', 'event_ticket_id', 'Registrations'),
185         'deadline': fields.date("Sales End"),
186         'is_expired': fields.function(_is_expired, type='boolean', string='Is Expired'),
187         'price': fields.float('Price'),
188         '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 )"),
189         'seats_reserved': fields.function(_get_seats, string='Reserved Seats', type='integer', multi='seats_reserved'),
190         'seats_available': fields.function(_get_seats, string='Available Seats', type='integer', multi='seats_reserved'),
191         'seats_unconfirmed': fields.function(_get_seats, string='Unconfirmed Seat Reservations', type='integer', multi='seats_reserved'),
192         'seats_used': fields.function(_get_seats, string='Number of Participations', type='integer', multi='seats_reserved'),
193     }
194
195     def _default_product_id(self, cr, uid, context={}):
196         imd = self.pool.get('ir.model.data')
197         try:
198             product = imd.get_object(cr, uid, 'event_sale', 'product_product_event')
199         except ValueError:
200             return False
201         return product.id
202
203     _defaults = {
204         'product_id': _default_product_id
205     }
206
207     def _check_seats_limit(self, cr, uid, ids, context=None):
208         for ticket in self.browse(cr, uid, ids, context=context):
209             if ticket.seats_max and ticket.seats_available < 0:
210                 return False
211         return True
212
213     _constraints = [
214         (_check_seats_limit, 'No more available tickets.', ['registration_ids','seats_max']),
215     ]
216
217     def onchange_product_id(self, cr, uid, ids, product_id=False, context=None):
218         return {'value': {'price': self.pool.get("product.product").browse(cr, uid, product_id).list_price or 0}}
219
220
221 class event_registration(osv.osv):
222     """Event Registration"""
223     _inherit= 'event.registration'
224     _columns = {
225         'event_ticket_id': fields.many2one('event.event.ticket', 'Event Ticket'),
226     }
227
228     def _check_ticket_seats_limit(self, cr, uid, ids, context=None):
229         for registration in self.browse(cr, uid, ids, context=context):
230             if registration.event_ticket_id.seats_max and registration.event_ticket_id.seats_available < 0:
231                 return False
232         return True
233
234     _constraints = [
235         (_check_ticket_seats_limit, 'No more available tickets.', ['event_ticket_id','nb_register','state']),
236     ]