[IMP] event: usability improvements
[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 import decimal_precision as dp
30
31
32 class event_type(osv.osv):
33     """ Event Type """
34     _name = 'event.type'
35     _description = __doc__
36     _columns = {
37         'name': fields.char('Event type', size=64, required=True),
38     }
39 event_type()
40
41 class event_event(osv.osv):
42     """Event"""
43     _name = 'event.event'
44     _description = __doc__
45     _order = 'date_begin'
46
47     def copy(self, cr, uid, id, default=None, context=None):
48         """ Copy record of Given id
49         @param id: Id of Event record.
50         @param context: A standard dictionary for contextual values
51         """
52         if not default:
53             default = {}
54         default.update({
55             'state': 'draft',
56             'registration_ids': False,
57         })
58         return super(event_event, self).copy(cr, uid, id, default=default, context=context)
59     def onchange_product(self, cr, uid, ids, product_id):
60         """This function returns value of  product's unit price based on product id.
61         @param self: The object pointer
62         @param cr: the current row, from the database cursor,
63         @param uid: the current user’s ID for security checks,
64         @param ids: List of Event IDs
65         @param product_id: Product's id
66         """
67         if not product_id:
68             return {'value': {'unit_price': False}}
69         else:
70            unit_price=self.pool.get('product.product').price_get(cr, uid, [product_id])[product_id]
71            return {'value': {'unit_price': unit_price}}
72
73     def button_draft(self, cr, uid, ids, context=None):
74         return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
75
76     def button_cancel(self, cr, uid, ids, context=None):
77         return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
78
79     def button_done(self, cr, uid, ids, context=None):
80         if type(ids) in (int, long,):
81             ids = [ids]
82         return self.write(cr, uid, ids, {'state': 'done'}, context=context)
83
84     def do_confirm(self, cr, uid, ids, context=None):
85         """ Confirm Event and send confirmation email to all register peoples
86         """
87         register_pool = self.pool.get('event.registration')
88         for event in self.browse(cr, uid, ids, context=context):
89             if event.mail_auto_confirm:
90                 #send reminder that will confirm the event for all the people that were already confirmed
91                 reg_ids = register_pool.search(cr, uid, [
92                                ('event_id', '=', event.id),
93                                ('state', 'not in', ['draft', 'cancel'])])
94                 register_pool.mail_user_confirm(cr, uid, reg_ids)
95
96         return self.write(cr, uid, ids, {'state': 'confirm'})
97
98     def button_confirm(self, cr, uid, ids, context=None):
99         """This Function Confirm Event.
100         @param ids: List of Event IDs
101         @param context: A standard dictionary for contextual values
102         @return: True
103         """
104         if not context:
105             context = {}
106         res = False
107         if type(ids) in (int, long,):
108             ids = [ids]
109         data_pool = self.pool.get('ir.model.data')
110         unconfirmed_ids = []
111         for event in self.browse(cr, uid, ids, context=context):
112             total_confirmed = event.register_current
113             if total_confirmed >= event.register_min or event.register_max == 0:
114                 res = self.do_confirm(cr, uid, [event.id], context=context)
115             else:
116                 unconfirmed_ids.append(event.id)
117         if unconfirmed_ids:
118             view_id = data_pool._get_id(cr, uid, 'event', 'view_event_confirm')
119             view_data = data_pool.browse(cr, uid, view_id)
120             view_id = view_data.res_id
121             context['event_ids'] = unconfirmed_ids
122             return {
123                 'name': _('Confirm Event'),
124                 'context': context,
125                 'view_type': 'form',
126                 'view_mode': 'tree,form',
127                 'res_model': 'event.confirm',
128                 'views': [(view_id, 'form')],
129                 'type': 'ir.actions.act_window',
130                 'target': 'new',
131                 'context': context,
132                 'nodestroy': True
133             }
134         return res
135
136
137     def _get_register(self, cr, uid, ids, fields, args, context=None):
138         """Get Confirm or uncofirm register value.
139         @param ids: List of Event registration type's id
140         @param fields: List of function fields(register_current and register_prospect).
141         @param context: A standard dictionary for contextual values
142         @return: Dictionary of function fields value.
143         """
144         register_pool = self.pool.get('event.registration')
145         res = {}
146         for event in self.browse(cr, uid, ids, context):
147             res[event.id] = {}
148             for field in fields:
149                 res[event.id][field] = False
150             state = []
151             if 'register_current' in fields:
152                 state += ['open', 'done']
153             if 'register_prospect' in fields:
154                 state.append('draft')
155
156             reg_ids = register_pool.search(cr, uid, [
157                         ('event_id', '=', event.id),
158                        ('state', 'in', state)])
159
160             if 'register_current' in fields:
161                 res[event.id]['register_current'] = len(reg_ids)
162             if 'register_prospect' in fields:
163                 res[event.id]['register_prospect'] = len(reg_ids)
164
165         return res
166
167     def write(self, cr, uid, ids, vals, context=None):
168         """
169         Writes values in one or several fields.
170         @param ids: List of Event registration type's IDs
171         @param vals: dictionary with values to update.
172         @return: True
173         """
174         register_pool = self.pool.get('event.registration')
175         res = super(event_event, self).write(cr, uid, ids, vals, context=context)
176         if vals.get('date_begin', False) or vals.get('mail_auto_confirm', False) or vals.get('mail_confirm', False):
177             for event in self.browse(cr, uid, ids, context=context):
178                 #change the deadlines of the registration linked to this event
179                 register_values = {}
180                 if vals.get('date_begin', False):
181                     register_values['date_deadline'] = vals['date_begin']
182
183                 #change the description of the registration linked to this event
184                 if vals.get('mail_auto_confirm', False):
185                     if vals['mail_auto_confirm']:
186                         if 'mail_confirm' not in vals:
187                             vals['mail_confirm'] = event.mail_confirm
188                     else:
189                         vals['mail_confirm'] = False
190                 if 'mail_confirm' in vals:
191                     register_values['description'] = vals['mail_confirm']
192
193                 if register_values:
194                     reg_ids = register_pool.search(cr, uid, [('event_id', '=', event.id)])
195                     register_pool.write(cr, uid, reg_ids, register_values)
196         return res
197
198     _columns = {
199         'name': fields.char('Summary', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
200         'user_id': fields.many2one('res.users', 'Responsible User', readonly=False, states={'done': [('readonly', True)]}),
201         'parent_id': fields.many2one('event.event', 'Parent Event', readonly=False, states={'done': [('readonly', True)]}),
202         'section_id': fields.many2one('crm.case.section', 'Sale Team', readonly=False, states={'done': [('readonly', True)]}),
203         'child_ids': fields.one2many('event.event', 'parent_id', 'Child Events', readonly=False, states={'done': [('readonly', True)]}),
204         'reply_to': fields.char('Reply-To', size=64, readonly=False, states={'done': [('readonly', True)]}, help="The email address put in the 'Reply-To' of all emails sent by OpenERP"),
205         'type': fields.many2one('event.type', 'Type', help="Type of Event like Seminar, Exhibition, Conference, Training.", readonly=False, states={'done': [('readonly', True)]}),
206         'register_max': fields.integer('Maximum Registrations', help="Provide Maximun Number of Registrations", readonly=True, states={'draft': [('readonly', False)]}),
207         'register_min': fields.integer('Minimum Registrations', help="Providee Minimum Number of Registrations", readonly=True, states={'draft': [('readonly', False)]}),
208         'register_current': fields.function(_get_register, method=True, string='Confirmed Registrations', multi='register_current',
209             help="Total of Open and Done Registrations"),
210         'register_prospect': fields.function(_get_register, method=True, string='Unconfirmed Registrations', multi='register_prospect',
211             help="Total of Prospect Registrations"),
212         'registration_ids': fields.one2many('event.registration', 'event_id', 'Registrations', readonly=False, states={'done': [('readonly', True)]}),
213         'date_begin': fields.datetime('Beginning date', required=True, help="Beginning Date of Event", readonly=True, states={'draft': [('readonly', False)]}),
214         'date_end': fields.datetime('Closing date', required=True, help="Closing Date of Event", readonly=True, states={'draft': [('readonly', False)]}),
215         'state': fields.selection([
216             ('draft', 'Draft'),
217             ('confirm', 'Confirmed'),
218             ('done', 'Done'),
219             ('cancel', 'Cancelled')],
220             'State', readonly=True, required=True,
221             help='If event is created, the state is \'Draft\'.If event is confirmed for the particular dates the state is set to \'Confirmed\'. If the event is over, the state is set to \'Done\'.If event is cancelled the state is set to \'Cancelled\'.'),
222         'mail_auto_registr': fields.boolean('Mail Auto Register', readonly=False, states={'done': [('readonly', True)]}, help='Check this box if you want to use the automatic mailing for new registration'),
223         'mail_auto_confirm': fields.boolean('Mail Auto Confirm', readonly=False, states={'done': [('readonly', True)]}, help='Check this box if you want ot use the automatic confirmation emailing or the reminder'),
224         'mail_registr': fields.text('Registration Email', readonly=False, states={'done': [('readonly', True)]}, help='This email will be sent when someone subscribes to the event.'),
225         'mail_confirm': fields.text('Confirmation Email', readonly=False, states={'done': [('readonly', True)]}, 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."),
226         'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="The invoices of this event registration will be created with this Product. Thus it allows you to set the default label and the accounting info you want by default on these invoices."),
227         'note': fields.text('Notes', help="Description or Summary of Event", readonly=False, states={'done': [('readonly', True)]}),
228         'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', readonly=True, states={'draft': [('readonly', False)]}, help="Pricelist version for current event."),
229         'unit_price': fields.related('product_id', 'list_price', type='float', string='Registration Cost', readonly=True, states={'draft':[('readonly',False)]}, help="This will be the default price used as registration cost when invoicing this event. Note that you can specify for each registration a specific amount if you want to", digits_compute=dp.get_precision('Sale Price')),
230         'main_speaker_id': fields.many2one('res.partner','Main Speaker', readonly=False, states={'done': [('readonly', True)]}, help="Speaker who are giving speech on event."),
231         'speaker_ids':fields.many2many('res.partner', 'event_speaker_rel', 'speaker_id', 'partner_id', 'Other Speakers', readonly=False, states={'done': [('readonly', True)]}),
232         'address_id': fields.many2one('res.partner.address','Location Address', readonly=False, states={'done': [('readonly', True)]}),
233         'speaker_confirmed': fields.boolean('Speaker Confirmed', readonly=False, states={'done': [('readonly', True)]}),
234         'country_id': fields.related('address_id', 'country_id',
235                     type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}),
236         'language': fields.char('Language',size=64, readonly=False, states={'done': [('readonly', True)]}),
237         'note': fields.text('Description', readonly=False, states={'done': [('readonly', True)]}),
238         'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=False, states={'done': [('readonly', True)]}),
239
240     }
241
242     _defaults = {
243         'state': 'draft',
244         'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'event.event', context=c),
245         'user_id': lambda obj, cr, uid, context: uid,
246     }
247
248     def _check_recursion(self, cr, uid, ids):
249         """
250         Checks for recursion level for event
251         """
252         level = 100
253
254         while len(ids):
255             cr.execute('select distinct parent_id from event_event where id IN %s', (tuple(ids),))
256             ids = filter(None, map(lambda x: x[0], cr.fetchall()))
257             if not level:
258                 return False
259             level -= 1
260
261         return True
262
263     _constraints = [
264         (_check_recursion, 'Error ! You cannot create recursive event.', ['parent_id'])
265     ]
266
267     def do_team_change(self, cr, uid, ids, team_id, context=None):
268         """
269         On Change Callback: when team change, this is call.
270         on this function, take value of reply_to from selected team.
271         """
272         if not team_id:
273             return {}
274         if context is None:
275             context = {}
276         team_pool = self.pool.get('crm.case.section')
277         team = team_pool.browse(cr, uid, team_id, context=context)
278         res = {}
279         if team.reply_to:
280             res = {'value': {'reply_to': team.reply_to}}
281         return res
282
283 event_event()
284
285 class event_registration(osv.osv):
286     """Event Registration"""
287     _name= 'event.registration'
288     _description = __doc__
289     _inherit = 'mailgate.thread'
290
291     def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
292         cur_obj = self.pool.get('res.currency')
293         res = {}
294         context = context or {}
295         for line in self.browse(cr, uid, ids, context=context):
296             price = line.unit_price * line.nb_register
297             pricelist = line.event_id.pricelist_id or line.partner_invoice_id.property_product_pricelist
298             cur = pricelist and pricelist.currency_id or False
299             res[line.id] = cur and cur_obj.round(cr, uid, cur, price) or price
300         return res
301
302     _columns = {
303         'name': fields.char('Summary', size=124,  readonly=True, states={'draft': [('readonly', False)]}),
304         'email_cc': fields.text('CC', size=252 , readonly=False, states={'done': [('readonly', True)]}, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
305         'nb_register': fields.integer('Quantity', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Number of Registrations or Tickets"),
306         'event_id': fields.many2one('event.event', 'Event', required=True, readonly=True, states={'draft': [('readonly', False)]}),
307         'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}),
308         "partner_invoice_id": fields.many2one('res.partner', 'Partner Invoiced', readonly=True, states={'draft': [('readonly', False)]}),
309         "contact_id": fields.many2one('res.partner.contact', 'Partner Contact', readonly=False, states={'done': [('readonly', True)]}), #TODO: filter only the contacts that have a function into the selected partner_id
310         "unit_price": fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Sale Price'), readonly=True, states={'draft': [('readonly', False)]}),
311         'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal', digits_compute=dp.get_precision('Sale Price')),
312         "badge_ids": fields.one2many('event.registration.badge', 'registration_id', 'Badges', readonly=False, states={'done': [('readonly', True)]}),
313         "event_product": fields.char("Invoice Name", size=128, readonly=True, states={'draft': [('readonly', False)]}),
314         "tobe_invoiced": fields.boolean("To be Invoiced", readonly=True, states={'draft': [('readonly', False)]}),
315         "invoice_id": fields.many2one("account.invoice", "Invoice", readonly=True),
316         'date_closed': fields.datetime('Closed', readonly=True),
317         'ref': fields.reference('Reference', selection=crm._links_get, size=128),
318         'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
319         'email_from': fields.char('Email', size=128, states={'done': [('readonly', True)]}, help="These people will receive email."),
320         'create_date': fields.datetime('Creation Date' , readonly=True),
321         'write_date': fields.datetime('Write Date' , readonly=True),
322         'description': fields.text('Description', states={'done': [('readonly', True)]}),
323         'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
324         'log_ids': fields.one2many('mailgate.message', 'res_id', 'Logs', domain=[('history', '=', False),('model','=',_name)]),
325         'date_deadline': fields.related('event_id','date_end', type='datetime', string="End Date", readonly=True),
326         'date': fields.related('event_id', 'date_begin', type='datetime', string="Start Date", readonly=True),
327         'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
328         'active': fields.boolean('Active'),
329         'section_id': fields.related('event_id', 'section_id', type='many2one', relation='crm.case.section', string='Sale Team', store=True, readonly=True),
330         'company_id': fields.related('event_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True, states={'draft':[('readonly',False)]}),
331         'state': fields.selection([('open', 'Confirmed'),
332                                     ('draft', 'Unconfirmed'),
333                                     ('cancel', 'Cancelled'),
334                                     ('done', 'Done')], 'State', \
335                                     size=16, readonly=True)
336     }
337     _defaults = {
338         'nb_register': 1,
339         'tobe_invoiced':  True,
340         'state': lambda *a: 'draft',
341         'active': lambda *a: 1,
342         'user_id': lambda self, cr, uid, ctx: uid,
343     }
344
345     def _make_invoice(self, cr, uid, reg, lines, context=None):
346         """ Create Invoice from Invoice lines
347         @param reg : Model of Event Registration
348         @param lines: Ids of Invoice lines
349         """
350         if context is None:
351             context = {}
352         inv_pool = self.pool.get('account.invoice')
353         inv_lines_pool = self.pool.get('account.invoice.line')
354
355         val_invoice = inv_pool.onchange_partner_id(cr, uid, [], 'out_invoice', reg.partner_invoice_id.id, False, False)
356         val_invoice['value'].update({'partner_id': reg.partner_invoice_id.id})
357         partner_address_id = val_invoice['value']['address_invoice_id']
358
359         value = inv_lines_pool.product_id_change(cr, uid, [], reg.event_id.product_id.id, uom =False, partner_id=reg.partner_invoice_id.id, fposition_id=reg.partner_invoice_id.property_account_position.id)
360
361         l = inv_lines_pool.read(cr, uid, lines)
362
363         val_invoice['value'].update({
364                 'origin': reg.event_product,
365                 'reference': False,
366                 'invoice_line': [(6, 0, lines)],
367                 'comment': "",
368                 'date_invoice': context.get('date_inv', False)
369             })
370         inv_id = inv_pool.create(cr, uid, val_invoice['value'])
371         inv_pool.button_compute(cr, uid, [inv_id])
372         self.history(cr, uid, [reg], _('Invoiced'))
373         return inv_id
374
375     def action_invoice_create(self, cr, uid, ids, grouped=False, date_inv = False, context=None):
376         """ Action of Create Invoice """
377         res = False
378         invoices = {}
379         tax_ids=[]
380         new_invoice_ids = []
381         inv_lines_pool = self.pool.get('account.invoice.line')
382         inv_pool = self.pool.get('account.invoice')
383         product_pool = self.pool.get('product.product')
384         contact_pool = self.pool.get('res.partner.contact')
385         if not context:
386             context = {}
387         # If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
388         # last day of the last month as invoice date
389         if date_inv:
390             context['date_inv'] = date_inv
391
392         for reg in self.browse(cr, uid, ids, context=context):
393
394             val_invoice = inv_pool.onchange_partner_id(cr, uid, [], 'out_invoice', reg.partner_invoice_id.id, False, False)
395
396             val_invoice['value'].update({'partner_id': reg.partner_invoice_id.id})
397             partner_address_id = val_invoice['value']['address_invoice_id']
398
399             if not partner_address_id:
400                raise osv.except_osv(_('Error !'),
401                         _("Registered partner doesn't have an address to make the invoice."))
402
403             value = inv_lines_pool.product_id_change(cr, uid, [], reg.event_id.product_id.id, uom =False, partner_id=reg.partner_invoice_id.id, fposition_id=reg.partner_invoice_id.property_account_position.id)
404             product = product_pool.browse(cr, uid, reg.event_id.product_id.id, context=context)
405             for tax in product.taxes_id:
406                 tax_ids.append(tax.id)
407
408             vals = value['value']
409             c_name = reg.contact_id and ('-' + contact_pool.name_get(cr, uid, [reg.contact_id.id])[0][1]) or ''
410             vals.update({
411                 'name': reg.event_product + '-' + c_name,
412                 'price_unit': reg.unit_price,
413                 'quantity': reg.nb_register,
414                 'product_id':reg.event_id.product_id.id,
415                 'invoice_line_tax_id': [(6, 0, tax_ids)],
416             })
417             inv_line_ids = self._create_invoice_lines(cr, uid, [reg.id], vals)
418             invoices.setdefault(reg.partner_id.id, []).append((reg, inv_line_ids))
419
420         for val in invoices.values():
421             res = False
422             if grouped:
423                 res = self._make_invoice(cr, uid, val[0][0], [v for k , v in val], context=context)
424
425                 for k , v in val:
426                     self.do_close(cr, uid, [k.id], context={'invoice_id': res})
427
428             else:
429                for k , v in val:
430                    res = self._make_invoice(cr, uid, k, [v], context=context)
431                    self.do_close(cr, uid, [k.id], context={'invoice_id': res})
432             if res: new_invoice_ids.append(res)
433
434         return new_invoice_ids
435
436     def do_open(self, cr, uid, ids, context=None):
437         """ Open Registration
438         """
439         res = self.write(cr, uid, ids, {'state': 'open'}, context=context)
440         self.mail_user(cr, uid, ids)
441         self.history(cr, uid, ids, _('Open'))
442         return res
443
444     def do_close(self, cr, uid, ids, context=None):
445         """ Close Registration
446         """
447         if not context:
448             context = {}
449         invoice_id = context.get('invoice_id', False)
450         values = {'state': 'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')}
451         msg = _('Done')
452         if invoice_id:
453             values['invoice_id'] = invoice_id
454         res = self.write(cr, uid, ids, values)
455         self.history(cr, uid, ids, msg)
456         return res
457
458     def check_confirm(self, cr, uid, ids, context=None):
459         """This Function Open Event Registration and send email to user.
460         @param ids: List of Event registration's IDs
461         @param context: A standard dictionary for contextual values
462         @return: True
463         """
464         if not context:
465             context = {}
466         data_pool = self.pool.get('ir.model.data')
467         unconfirmed_ids = []
468         for registration in self.browse(cr, uid, ids, context=context):
469             total_confirmed = registration.event_id.register_current + registration.nb_register
470             if total_confirmed <= registration.event_id.register_max or registration.event_id.register_max == 0:
471                 self.do_open(cr, uid, [registration.id], context)
472             else:
473                 unconfirmed_ids.append(registration.id)
474         if unconfirmed_ids:
475             view_id = data_pool._get_id(cr, uid, 'event', 'view_event_confirm_registration')
476             view_data = data_pool.browse(cr, uid, view_id)
477             view_id = view_data.res_id
478             context['registration_ids'] = unconfirmed_ids
479             return {
480                 'name': _('Confirm Registration'),
481                 'context': context,
482                 'view_type': 'form',
483                 'view_mode': 'tree,form',
484                 'res_model': 'event.confirm.registration',
485                 'views': [(view_id, 'form')],
486                 'type': 'ir.actions.act_window',
487                 'target': 'new',
488                 'context': context,
489                 'nodestroy': True
490             }
491         return True
492
493     def button_reg_close(self, cr, uid, ids, context=None):
494         """This Function Close Event Registration.
495         """
496         if not context:
497             context = {}
498         data_pool = self.pool.get('ir.model.data')
499         unclosed_ids = []
500         for registration in self.browse(cr, uid, ids, context=context):
501             if registration.tobe_invoiced and not registration.invoice_id:
502                 unclosed_ids.append(registration.id)
503             else:
504                 self.do_close(cr, uid, [registration.id])
505         if unclosed_ids:
506             view_id = data_pool._get_id(cr, uid, 'event', 'view_event_make_invoice')
507             view_data = data_pool.browse(cr, uid, view_id)
508             view_id = view_data.res_id
509             context['active_ids'] = unclosed_ids
510             return {
511                 'name': _('Close Registration'),
512                 'context': context,
513                 'view_type': 'form',
514                 'view_mode': 'tree,form',
515                 'res_model': 'event.make.invoice',
516                 'views': [(view_id, 'form')],
517                 'type': 'ir.actions.act_window',
518                 'target': 'new',
519                 'context': context,
520                 'nodestroy': True
521             }
522         return True
523
524     def button_reg_cancel(self, cr, uid, ids, *args):
525         """This Function Cancel Event Registration.
526         """
527         registrations = self.browse(cr, uid, ids)
528         self.history(cr, uid, registrations, _('Cancel'))
529         self.write(cr, uid, ids, {'state': 'cancel'})
530         return True
531
532     def mail_user(self, cr, uid, ids, confirm=False, context=None):
533         """
534         Send email to user
535         """
536         if not context:
537             context = {}
538
539         for regestration in self.browse(cr, uid, ids, context=context):
540             src = regestration.event_id.reply_to or False
541             email_to = []
542             email_cc = []
543             if regestration.email_from:
544                 email_to = regestration.email_from
545             if regestration.email_cc:
546                 email_cc += [regestration.email_cc]
547             if not (email_to or email_cc):
548                 continue
549             subject = ""
550             body = ""
551             if confirm:
552                 subject = _('Auto Confirmation: [%s] %s') %(regestration.id, regestration.name)
553                 body = regestration.event_id.mail_confirm
554             elif regestration.event_id.mail_auto_confirm or regestration.event_id.mail_auto_registr:
555                 if regestration.event_id.state in ['draft', 'fixed', 'open', 'confirm', 'running'] and regestration.event_id.mail_auto_registr:
556                     subject = _('Auto Registration: [%s] %s') %(regestration.id, regestration.name)
557                     body = regestration.event_id.mail_registr
558                 if (regestration.event_id.state in ['confirm', 'running']) and regestration.event_id.mail_auto_confirm:
559                     subject = _('Auto Confirmation: [%s] %s') %(regestration.id, regestration.name)
560                     body = regestration.event_id.mail_confirm
561             if subject or body:
562                 tools.email_send(src, email_to, subject, body, email_cc = email_cc, openobject_id = regestration.id)
563                 self.history(cr, uid, [regestration], subject, history=True, \
564                         email=email_to, details=body, \
565                         subject=subject, email_from=src, \
566                         email_cc=', '.join(email_cc))
567
568         return True
569
570     def mail_user_confirm(self, cr, uid, ids, context=None):
571         """
572         Send email to user
573         """
574         return self.mail_user(cr, uid, ids, confirm=True, context=context)
575
576     def _create_invoice_lines(self, cr, uid, ids, vals):
577         """ Create account Invoice line for Registration Id.
578         """
579         return self.pool.get('account.invoice.line').create(cr, uid, vals)
580
581     def onchange_contact_id(self, cr, uid, ids, contact, partner):
582
583         """This function returns value of Badge Name , Badge Title based on Partner contact.
584         @param self: The object pointer
585         @param cr: the current row, from the database cursor,
586         @param uid: the current user’s ID for security checks,
587         @param ids: List of Registration IDs
588         @param contact: Patner Contact IDS
589         @param partner: Partner IDS
590         """
591         data ={}
592         if not contact:
593             return data
594         contact_obj = self.pool.get('res.partner.contact')
595         addr_obj = self.pool.get('res.partner.address')
596         job_obj = self.pool.get('res.partner.job')
597
598         contact_id = contact_obj.browse(cr, uid, contact)
599         if partner:
600             partner_addresses = addr_obj.search(cr, uid, [('partner_id', '=', partner)])
601             job_ids = job_obj.search(cr, uid, [('contact_id', '=', contact), ('address_id', 'in', partner_addresses)])
602             if job_ids:
603                 data['email_from'] = job_obj.browse(cr, uid, job_ids[0]).email
604         return {'value': data}
605
606     def onchange_event(self, cr, uid, ids, event_id, partner_invoice_id):
607         """This function returns value of Product Name, Unit Price based on Event.
608         @param self: The object pointer
609         @param cr: the current row, from the database cursor,
610         @param uid: the current user’s ID for security checks,
611         @param ids: List of Registration IDs
612         @param event_id: Event ID
613         @param partner_invoice_id: Partner Invoice ID
614         """
615         context={}
616         if not event_id:
617             return {'value': {'unit_price': False, 'event_product': False}}
618
619         event_obj = self.pool.get('event.event')
620         prod_obj = self.pool.get('product.product')
621         res_obj = self.pool.get('res.partner')
622
623         data_event =  event_obj.browse(cr, uid, event_id)
624         res = {'value': {'unit_price': False, 'event_product': False, 'user_id': False,
625                         'date': data_event.date_begin, 'date_deadline': data_event.date_end, 'description': data_event.note, 'name': data_event.name,
626                         'section_id': data_event.section_id and data_event.section_id.id or False,
627                         }}
628         if data_event.user_id.id:
629             res['value'].update({'user_id':data_event.user_id.id})
630         if data_event.product_id:
631             pricelist_id = data_event.pricelist_id and data_event.pricelist_id.id or False
632             if partner_invoice_id:
633                 partner = res_obj.browse(cr, uid, partner_invoice_id, context=context)
634                 pricelist_id = pricelist_id or partner.property_product_pricelist.id
635             unit_price = prod_obj._product_price(cr, uid, [data_event.product_id.id], False, False, {'pricelist': pricelist_id})[data_event.product_id.id]
636             if not unit_price:
637                 unit_price = data_event.unit_price
638             res['value'].update({'unit_price': unit_price, 'event_product': data_event.product_id.name})
639         return res
640
641     def onchange_partner_id(self, cr, uid, ids, part, event_id, email=False):
642         """This function returns value of Patner Invoice id, Unit Price, badget title based on partner and Event.
643         @param self: The object pointer
644         @param cr: the current row, from the database cursor,
645         @param uid: the current user’s ID for security checks,
646         @param ids: List of Registration IDs
647         @param event_id: Event ID
648         @param partner_invoice_id: Partner Invoice ID
649         """
650         job_obj = self.pool.get('res.partner.job')
651         res_obj = self.pool.get('res.partner')
652
653         data = {}
654         data['contact_id'], data['partner_invoice_id'], data['email_from'] = (False, False, False)
655         if not part:
656             return {'value': data}
657         data['partner_invoice_id']=part
658         # this calls onchange_partner_invoice_id
659         d = self.onchange_partner_invoice_id(cr, uid, ids, event_id, part)
660         # this updates the dictionary
661         data.update(d['value'])
662         addr = res_obj.address_get(cr, uid, [part])
663         if addr:
664             if addr.has_key('default'):
665                 job_ids = job_obj.search(cr, uid, [('address_id', '=', addr['default'])])
666                 if job_ids:
667                     data['contact_id'] = job_obj.browse(cr, uid, job_ids[0]).contact_id.id
668                     d = self.onchange_contact_id(cr, uid, ids, data['contact_id'], part)
669                     data.update(d['value'])
670         partner_data = res_obj.browse(cr, uid, part)
671         return {'value': data}
672
673     def onchange_partner_invoice_id(self, cr, uid, ids, event_id, partner_invoice_id):
674         """This function returns value of Product unit Price based on Invoiced partner.
675         @param self: The object pointer
676         @param cr: the current row, from the database cursor,
677         @param uid: the current user’s ID for security checks,
678         @param ids: List of Registration IDs
679         @param event_id: Event ID
680         @param partner_invoice_id: Partner Invoice ID
681         """
682         data={}
683         context={}
684         event_obj = self.pool.get('event.event')
685         prod_obj = self.pool.get('product.product')
686         res_obj = self.pool.get('res.partner')
687
688         data['unit_price']=False
689         if not event_id:
690             return {'value': data}
691         data_event =  event_obj.browse(cr, uid, event_id)
692         if data_event.product_id:
693             data['event_product'] = data_event.product_id.name
694             pricelist_id = data_event.pricelist_id and data_event.pricelist_id.id or False
695             if partner_invoice_id:
696                 partner = res_obj.browse(cr, uid, partner_invoice_id, context=context)
697                 pricelist_id = pricelist_id or partner.property_product_pricelist.id
698             unit_price = prod_obj._product_price(cr, uid, [data_event.product_id.id], False, False, {'pricelist': pricelist_id})[data_event.product_id.id]
699             if not unit_price:
700                 unit_price = data_event.unit_price
701             data['unit_price'] = unit_price
702         return {'value': data}
703
704 event_registration()
705
706 class event_registration_badge(osv.osv):
707     _name = 'event.registration.badge'
708     _description = __doc__
709     _columns = {
710         'registration_id': fields.many2one('event.registration', 'Registration', required=True),
711         "title": fields.char('Title', size=128),
712         "name": fields.char('Name', size=128, required=True),
713         "address_id": fields.many2one('res.partner.address', 'Address'),
714     }
715 event_registration_badge()
716
717 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
718