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