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