1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
23 from osv import fields, osv
24 from tools.translate import _
31 class event_type(osv.osv):
36 _description = __doc__
38 'name': fields.char('Event type', size=64, required=True),
48 _description = __doc__
49 _inherit = 'crm.case.section'
52 def copy(self, cr, uid, id, default=None, context=None):
55 Copy record of Given id
56 @param self: The object pointer
57 @param cr: the current row, from the database cursor,
58 @param uid: the current user’s ID for security checks,
59 @param id: Id of Event Registration type record.
60 @param context: A standard dictionary for contextual values
63 return super(event, self).copy(cr, uid, id, default={'code': self.pool.get('ir.sequence').get(cr, uid, 'event.event'), 'state': 'draft'})
65 def button_draft(self, cr, uid, ids, context={}):
66 return self.write(cr, uid, ids, {'state': 'draft'})
68 def button_cancel(self, cr, uid, ids, context={}):
69 return self.write(cr, uid, ids, {'state': 'cancel'})
71 def button_done(self, cr, uid, ids, context={}):
72 return self.write(cr, uid, ids, {'state': 'done'})
74 def button_confirm(self, cr, uid, ids, context={}):
76 for eve in self.browse(cr, uid, ids):
77 if eve.mail_auto_confirm:
78 #send reminder that will confirm the event for all the people that were already confirmed
79 reg_ids = self.pool.get('event.registration').search(cr, uid,
80 [('event_id', '=', eve.id),
81 ('state', 'not in', ['draft', 'cancel'])])
83 self.pool.get('event.registration').mail_user_confirm(cr, uid, reg_ids)
85 return self.write(cr, uid, ids, {'state': 'confirm'})
92 def _get_register(self, cr, uid, ids, name, args, context=None):
95 Get Confirm or uncofirm register value.
96 @param self: The object pointer
97 @param cr: the current row, from the database cursor,
98 @param uid: the current user’s ID for security checks,
99 @param ids: List of Event registration type's id
100 @param name: List of function fields(register_current and register_prospect).
101 @param context: A standard dictionary for contextual values
102 @return: Dictionary of function fields value.
106 for event in self.browse(cr, uid, ids, context):
109 if name[0] == 'register_current':
111 query = """SELECT sum(r.nb_register)
112 from event_registration r
113 where state=%s and event_id=%s"""
115 cr.execute(query, (state, event.id,))
119 res[event.id][name[0]] = res2[0]
121 res[event.id][name[0]] = 0
125 def write(self, cr, uid, ids, vals, *args, **kwargs):
127 Writes values in one or several fields.
128 @param cr: the current row, from the database cursor,
129 @param uid: the current user’s ID for security checks,
130 @param ids: List of Event registration type's IDs
131 @param vals: dictionary with values to update.
134 res = super(event, self).write(cr, uid, ids, vals, *args, **kwargs)
136 if 'date_begin' in vals and vals['date_begin']:
138 for eve in self.browse(cr, uid, ids):
139 #change the deadlines of the registration linked to this event
140 reg_ids = self.pool.get('event.registration').search(cr, uid,
141 [('event_id', '=', eve.id)])
143 self.pool.get('event.registration').write(cr, uid, reg_ids,
144 {'date_deadline': vals['date_begin']})
146 #change the description of the registration linked to this event
147 if 'mail_auto_confirm' in vals:
148 if vals['mail_auto_confirm']:
149 if 'mail_confirm' not in vals:
150 for eve in self.browse(cr, uid, ids):
151 vals['mail_confirm'] = eve.mail_confirm
153 vals['mail_confirm'] = False
154 if 'mail_confirm' in vals:
155 for eve in self.browse(cr, uid, ids):
156 reg_ids = self.pool.get('event.registration').search(cr, uid,
157 [('event_id', '=', eve.id)])
159 self.pool.get('event.registration').write(cr, uid, reg_ids,
160 {'description': vals['mail_confirm']})
164 'type': fields.many2one('event.type', 'Type'),
165 'register_max': fields.integer('Maximum Registrations'),
166 'register_min': fields.integer('Minimum Registrations'),
167 'register_current': fields.function(_get_register, method=True, string='Confirmed Registrations', multi='register_current'),
168 'register_prospect': fields.function(_get_register, method=True, string='Unconfirmed Registrations', multi='register_prospect'),
169 'date_begin': fields.datetime('Beginning date', required=True),
170 'date_end': fields.datetime('Ending date', required=True),
171 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')], 'State', readonly=True, required=True,
172 help='If event is created, the state is \'Draft\'.\n If event is confirmed for the particular dates the state is set to \'Confirmed\'.\
173 \nIf the event is over, the state is set to \'Done\'.\n If event is cancelled the state is set to \'Cancelled\'.'),
174 'mail_auto_registr': fields.boolean('Mail Auto Register', help='Check this box if you want to use the automatic mailing for new registration'),
175 'mail_auto_confirm': fields.boolean('Mail Auto Confirm', help='Check this box if you want ot use the automatic confirmation emailing or the reminder'),
176 'mail_registr': fields.text('Registration Email', help='This email will be sent when someone subscribes to the event.'),
177 'mail_confirm': fields.text('Confirmation Email', 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."),
178 'product_id': fields.many2one('product.product', 'Product', required=True),
179 'note': fields.text('Notes')
183 'state': lambda *args: 'draft',
184 # 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'event.event'),
185 'user_id': lambda self, cr, uid, ctx: uid,
190 class event_registration(osv.osv):
192 """Event Registration"""
194 def _make_invoice(self, cr, uid, reg, lines, context=None):
198 inv_obj = self.pool.get('account.invoice')
200 obj_lines = self.pool.get('account.invoice.line')
202 val_invoice = inv_obj.onchange_partner_id(cr, uid, [], 'out_invoice', reg.partner_invoice_id.id, False, False)
204 val_invoice['value'].update({'partner_id': reg.partner_invoice_id.id})
205 partner_address_id = val_invoice['value']['address_invoice_id']
206 value = obj_lines.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)
208 l = obj_lines.read(cr, uid, lines)
210 val_invoice['value'].update({
211 'origin': reg.event_product,
213 'invoice_line': [(6, 0, lines)],
216 inv_id = inv_obj.create(cr, uid, val_invoice['value'])
218 self._history(cr, uid, [reg], _('Invoiced'))
220 inv_obj.button_compute(cr, uid, [inv_id])
223 def action_invoice_create(self, cr, uid, ids, grouped=False, date_inv = False):
229 obj_reg = self.pool.get('event.registration')
230 obj_lines = self.pool.get('account.invoice.line')
231 inv_obj = self.pool.get('account.invoice')
234 # If date was specified, use it as date invoiced, usefull when invoices are generated this month and put the
235 # last day of the last month as invoice date
237 context['date_inv'] = date_inv
239 obj_event_reg = self.pool.get('event.registration')
240 obj_lines = self.pool.get('account.invoice.line')
241 inv_obj = self.pool.get('account.invoice')
242 data_event_reg = obj_event_reg.browse(cr, uid, ids, context=context)
244 for reg in data_event_reg:
246 val_invoice = inv_obj.onchange_partner_id(cr, uid, [], 'out_invoice', reg.partner_invoice_id.id, False, False)
248 val_invoice['value'].update({'partner_id': reg.partner_invoice_id.id})
249 partner_address_id = val_invoice['value']['address_invoice_id']
251 if not partner_address_id:
252 raise osv.except_osv(_('Error !'),
253 _("Registered partner doesn't have an address to make the invoice."))
255 value = obj_lines.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)
256 data_product = self.pool.get('product.product').browse(cr, uid, [reg.event_id.product_id.id])
257 for tax in data_product[0].taxes_id:
258 tax_ids.append(tax.id)
260 vals = value['value']
261 c_name = reg.contact_id and ('-' + self.pool.get('res.partner.contact').name_get(cr, uid, [reg.contact_id.id])[0][1]) or ''
263 'name': reg.event_product + '-' + c_name,
264 'price_unit': reg.unit_price,
265 'quantity': reg.nb_register,
266 'product_id':reg.event_id.product_id.id,
267 'invoice_line_tax_id': [(6, 0, tax_ids)],
269 inv_line_ids = obj_event_reg._create_invoice_lines(cr, uid, [reg.id], vals)
270 invoices.setdefault(reg.partner_id.id, []).append((reg, inv_line_ids))
272 for val in invoices.values():
274 res = self._make_invoice(cr, uid, val[0][0], [v for k , v in val], context=context)
277 self.write(cr, uid, [k.id], {'state': 'done', 'invoice_id': res})
281 res = self._make_invoice(cr, uid, k, [v], context=context)
282 self.write(cr, uid, [k.id], {'state': 'done', 'invoice_id': res})
283 #res = self._make_invoice(cr, uid, val[0][0], m, context=context)
286 def check_confirm(self, cr, uid, ids, context):
289 Check confirm event register on given id.
290 @param cr: the current row, from the database cursor,
291 @param uid: the current user’s ID for security checks,
292 @param ids: List of Event registration's IDs
293 @param context: A standard dictionary for contextual values
294 @return: Dictionary value which open Confirm registration form.
296 registration_obj = self.browse(cr, uid, ids)
297 self._history(cr, uid, registration_obj, _('Open'))
298 mod_obj = self.pool.get('ir.model.data')
300 for current_registration in registration_obj:
302 total_confirmed = current_registration.event_id.register_current + current_registration.nb_register
303 if total_confirmed <= current_registration.event_id.register_max or current_registration.event_id.register_max == 0:
304 self.write(cr, uid, ids, {'state': 'open'}, context=context)
305 self.mail_user(cr, uid, ids)
309 model_data_ids = mod_obj.search(cr, uid, [('model', '=', 'ir.ui.view'), ('name', '=', 'view_event_confirm_registration')], context=context)
310 resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
312 context.update({'reg_id': id})
315 'name': _('Confirm Registration'),
318 'view_mode': 'tree,form',
319 'res_model': 'event.confirm.registration',
320 'views': [(resource_id, 'form')],
321 'type': 'ir.actions.act_window',
326 def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=[], context={}):
327 mailgate_pool = self.pool.get('mailgate.thread')
328 return mailgate_pool._history(cr, uid, cases, keyword, history=history,\
329 subject=subject, email=email, \
330 details=details, email_from=email_from,\
331 message_id=message_id, attach=attach, \
334 def button_reg_close(self, cr, uid, ids, *args):
336 cases = self.browse(cr, uid, ids)
337 self._history(cr, uid, cases, _('Done'))
338 self.write(cr, uid, ids, {'state': 'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
341 def button_reg_cancel(self, cr, uid, ids, *args):
343 cases = self.browse(cr, uid, ids)
344 self._history(cr, uid, cases, _('Cancel'))
345 self.write(cr, uid, ids, {'state': 'cancel'})
348 def create(self, cr, uid, *args, **argv):
349 """ Overrides orm create method.
350 @param self: The object pointer
351 @param cr: the current row, from the database cursor,
352 @param uid: the current user’s ID for security checks,
353 @param *args: Fields value
354 @return : New created record Id.
357 event = self.pool.get('event.event').browse(cr, uid, args[0]['event_id'], None)
359 args[0]['date_deadline']= event.date_begin
360 args[0]['description']= event.mail_confirm
361 res = super(event_registration, self).create(cr, uid, *args, **argv)
362 cases = self.browse(cr, uid, [res])
363 self._history(cr, uid, cases, _('Created'))
366 def write(self, cr, uid, *args, **argv):
368 if 'event_id' in args[1]:
369 event = self.pool.get('event.event').browse(cr, uid, args[1]['event_id'], None)
371 args[1]['date_deadline']= event.date_begin
372 args[1]['description']= event.mail_confirm
373 return super(event_registration, self).write(cr, uid, *args, **argv)
376 def remind_partner(self, cr, uid, ids, context={}, attach=False):
379 @param self: The object pointer
380 @param cr: the current row, from the database cursor,
381 @param uid: the current user’s ID for security checks,
382 @param ids: List of Remind Partner's IDs
383 @param context: A standard dictionary for contextual values
386 return self.remind_user(cr, uid, ids, context, attach,
390 def remind_user(self, cr, uid, ids, context={}, attach=False,destination=True):
393 @param self: The object pointer
394 @param cr: the current row, from the database cursor,
395 @param uid: the current user’s ID for security checks,
396 @param ids: List of Remind user's IDs
397 @param context: A standard dictionary for contextual values
400 for case in self.browse(cr, uid, ids):
402 if not case.event_id.reply_to:
403 raise osv.except_osv(_('Error!'), ("Reply To is not specified for selected Event"))
404 if not case.email_from:
405 raise osv.except_osv(_('Error!'), ("Partner Email is not specified in Registration"))
406 if case.event_id.reply_to and case.email_from:
407 src = case.email_from
408 dest = case.event_id.reply_to
410 body = case.description
412 src, dest = dest, src
413 if body and case.user_id.signature:
414 body += '\n\n%s' % (case.user_id.signature)
416 body = self.format_body(body)
419 attach_to_send = None
422 attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', 'mailgate.thread'), ('res_id', '=', case.id)])
423 attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname','datas'])
424 attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)
427 flag = tools.email_send(
430 "Reminder: [%s] %s" % (str(case.id), case.name,),
432 reply_to = case.event_id.reply_to,
433 openobject_id=str(case.id),
434 attach=attach_to_send
436 self._history(cr, uid, [case], _('Send'), history=True, email=dest, details=body, email_from=src)
439 # raise osv.except_osv(_('Email!'),("Email Successfully Sent"))
441 # raise osv.except_osv(_('Email Fail!'),("Email is not sent successfully"))
444 def mail_user_confirm(self, cr, uid, ids):
446 @param self: The object pointer
447 @param cr: the current row, from the database cursor,
448 @param uid: the current user’s ID for security checks,
449 @param ids: List of Event Registration's Id.
452 reg_ids = self.browse(cr, uid, ids)
453 for reg_id in reg_ids:
454 src = reg_id.event_id.reply_to or False
456 if reg_id.email_from:
457 dest += [reg_id.email_from]
459 dest += [reg_id.email_cc]
461 tools.email_send(src, dest, 'Auto Confirmation: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_confirm, openobject_id = str(reg_id.id))
463 raise osv.except_osv(_('Error!'), _('You must define a reply-to address in order to mail the participant. You can do this in the Mailing tab of your event. Note that this is also the place where you can configure your event to not send emails automaticly while registering'))
467 def mail_user(self, cr, uid, ids):
469 @param self: The object pointer
470 @param cr: the current row, from the database cursor,
471 @param uid: the current user’s ID for security checks,
472 @param ids: List of Event Registration's Id.
475 reg_ids = self.browse(cr, uid, ids)
477 for reg_id in reg_ids:
478 src = reg_id.event_id.reply_to or False
480 if reg_id.email_from:
481 dest += [reg_id.email_from]
483 dest += [reg_id.email_cc]
484 if reg_id.event_id.mail_auto_confirm or reg_id.event_id.mail_auto_registr:
486 if reg_id.event_id.state in ['draft', 'fixed', 'open', 'confirm', 'running'] and reg_id.event_id.mail_auto_registr:
487 tools.email_send(src, dest, 'Auto Registration: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_registr, openobject_id = str(reg_id.id))
488 if (reg_id.event_id.state in ['confirm', 'running']) and reg_id.event_id.mail_auto_confirm:
489 tools.email_send(src, dest, 'Auto Confirmation: '+'['+str(reg_id.id)+']'+' '+reg_id.name, reg_id.event_id.mail_confirm, openobject_id = str(reg_id.id))
491 raise osv.except_osv(_('Error!'), _('You must define a reply-to address in order to mail the participant. You can do this in the Mailing tab of your event. Note that this is also the place where you can configure your event to not send emails automaticly while registering'))
494 def _create_invoice_lines(self, cr, uid, ids, vals):
496 """ Create account Invoice line for Registration Id.
497 @param self: The object pointer
498 @param cr: the current row, from the database cursor,
499 @param uid: the current user’s ID for security checks,
500 @param Ids: List of event registration's Id
501 @param vals: Create fields value
502 @return : New created record Id.
505 return self.pool.get('account.invoice.line').create(cr, uid, vals)
507 _name= 'event.registration'
508 _description = __doc__
509 _inherit = 'crm.meeting'
512 'email_cc': fields.text('CC', size=252 , help="These \
513 people will receive a copy of the future communication between partner \
514 and users by email"),
515 'nb_register': fields.integer('Number of Registration', readonly=True, states={'draft': [('readonly', False)]}),
516 'event_id': fields.many2one('event.event', 'Event Related', required=True),
517 "partner_invoice_id": fields.many2one('res.partner', 'Partner Invoiced'),
518 "contact_id": fields.many2one('res.partner.contact', 'Partner Contact'), #TODO: filter only the contacts that have a function into the selected partner_id
519 "unit_price": fields.float('Unit Price'),
520 "badge_title": fields.char('Badge Title', size=128),
521 "badge_name": fields.char('Badge Name', size=128),
522 "badge_partner": fields.char('Badge Partner', size=128),
523 "event_product": fields.char("Product Name", size=128, required=True),
524 "tobe_invoiced": fields.boolean("To be Invoiced"),
525 "invoice_id": fields.many2one("account.invoice", "Invoice"),
526 'date_closed': fields.datetime('Closed', readonly=True),
527 'ref': fields.reference('Reference', selection=crm._links_get, size=128),
528 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
529 'canal_id': fields.many2one('res.partner.canal', 'Channel', help="The channels represent the different communication modes available with the customer." \
530 " With each commercial opportunity, you can indicate the canall which is this opportunity source."),
531 'som': fields.many2one('res.partner.som', 'State of Mind', help="The minds states allow to define a value scale which represents" \
532 "the partner mentality in relation to our services.The scale has" \
533 "to be created with a factor for each level from 0 (Very dissatisfied) to 10 (Extremely satisfied)."),
537 'nb_register': lambda *a: 1,
538 'tobe_invoiced': lambda *a: True,
539 'name': lambda *a: 'Registration',
542 def onchange_badge_name(self, cr, uid, ids, badge_name):
547 data['name'] = 'Registration: ' + badge_name
548 return {'value': data}
550 def onchange_contact_id(self, cr, uid, ids, contact, partner):
556 contact_id = self.pool.get('res.partner.contact').browse(cr, uid, contact)
557 data['badge_name'] = contact_id.name
558 data['badge_title'] = contact_id.title
560 partner_addresses = self.pool.get('res.partner.address').search(cr, uid, [('partner_id', '=', partner)])
561 job_ids = self.pool.get('res.partner.job').search(cr, uid, [('contact_id', '=', contact), ('address_id', 'in', partner_addresses)])
563 data['email_from'] = self.pool.get('res.partner.job').browse(cr, uid, job_ids[0]).email
564 d = self.onchange_badge_name(cr, uid, ids, data['badge_name'])
565 data.update(d['value'])
566 return {'value': data}
568 def onchange_event(self, cr, uid, ids, event_id, partner_invoice_id):
571 return {'value': {'unit_price': False, 'event_product': False}}
572 data_event = self.pool.get('event.event').browse(cr, uid, event_id)
574 if data_event.product_id:
575 if not partner_invoice_id:
576 unit_price=self.pool.get('product.product').price_get(cr, uid, [data_event.product_id.id], context=context)[data_event.product_id.id]
577 return {'value': {'unit_price': unit_price, 'event_product': data_event.product_id.name}}
578 data_partner = self.pool.get('res.partner').browse(cr, uid, partner_invoice_id)
579 context.update({'partner_id': data_partner})
580 unit_price = self.pool.get('product.product')._product_price(cr, uid, [data_event.product_id.id], False, False, {'pricelist': data_partner.property_product_pricelist.id})[data_event.product_id.id]
581 return {'value': {'unit_price': unit_price, 'event_product': data_event.product_id.name}}
583 return {'value': {'unit_price': False, 'event_product': False}}
585 def onchange_partner_id(self, cr, uid, ids, part, event_id, email=False):
588 data['badge_partner'] = data['contact_id'] = data['partner_invoice_id'] = data['email_from'] = data['badge_title'] = data['badge_name'] = False
590 return {'value': data}
591 data['partner_invoice_id']=part
592 # this calls onchange_partner_invoice_id
593 d = self.onchange_partner_invoice_id(cr, uid, ids, event_id, part)
594 # this updates the dictionary
595 data.update(d['value'])
596 addr = self.pool.get('res.partner').address_get(cr, uid, [part])
598 if addr.has_key('default'):
599 job_ids = self.pool.get('res.partner.job').search(cr, uid, [('address_id', '=', addr['default'])])
601 data['contact_id'] = self.pool.get('res.partner.job').browse(cr, uid, job_ids[0]).contact_id.id
602 d = self.onchange_contact_id(cr, uid, ids, data['contact_id'], part)
603 data.update(d['value'])
604 partner_data = self.pool.get('res.partner').browse(cr, uid, part)
605 data['badge_partner'] = partner_data.name
606 return {'value': data}
608 def onchange_partner_invoice_id(self, cr, uid, ids, event_id, partner_invoice_id):
612 data['unit_price']=False
614 return {'value': data}
615 data_event = self.pool.get('event.event').browse(cr, uid, event_id)
617 if data_event.product_id:
618 if not partner_invoice_id:
619 data['unit_price']=self.pool.get('product.product').price_get(cr, uid, [data_event.product_id.id], context=context)[data_event.product_id.id]
620 return {'value': data}
621 data_partner = self.pool.get('res.partner').browse(cr, uid, partner_invoice_id)
622 context.update({'partner_id': data_partner})
623 data['unit_price'] = self.pool.get('product.product')._product_price(cr, uid, [data_event.product_id.id], False, False, {'pricelist': data_partner.property_product_pricelist.id})[data_event.product_id.id]
624 return {'value': data}
625 return {'value': data}
630 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: