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