diff --git a/addons/base_calendar/__init__.py b/addons/base_calendar/__init__.py index 425fb2ed375..6b05334366e 100644 --- a/addons/base_calendar/__init__.py +++ b/addons/base_calendar/__init__.py @@ -20,7 +20,6 @@ ############################################################################## import base_calendar -import crm_meeting import controllers # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/base_calendar/__openerp__.py b/addons/base_calendar/__openerp__.py index fd27979e600..18d91036429 100644 --- a/addons/base_calendar/__openerp__.py +++ b/addons/base_calendar/__openerp__.py @@ -42,10 +42,8 @@ If you need to manage your meetings, you should install the CRM module. 'data': [ 'security/calendar_security.xml', 'security/ir.model.access.csv', - #'base_calendar_view.xml', 'crm_meeting_view.xml', - #'base_calendar_data.xml', - 'crm_meeting_data.xml', + 'base_calendar_data.xml' ], 'js': [ 'static/src/js/*.js' diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index bfb758651fa..8cafc0a331c 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -19,21 +19,28 @@ # ############################################################################## +import hashlib +import pytz +import re +import time +import openerp +import openerp.service.report + from datetime import datetime, timedelta, date from dateutil import parser from dateutil import rrule from dateutil.relativedelta import relativedelta -from openerp.osv import fields, osv -from openerp.tools.translate import _ -import pytz -import re -import time -from openerp import tools -import openerp.service.report +from openerp import tools, SUPERUSER_ID +from openerp.osv import fields, osv +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT +from openerp.tools.translate import _ + + + def get_recurrent_dates(rrulestring, startdate, exdate=None, tz=None, context=None): """Get recurrent dates based on Rule string considering exdate and start date. - + All input dates and output dates are in UTC. Dates are infered thanks to rules in the ``tz`` timezone if given, else it'll be in the current local timezone as specified in the context. @@ -51,16 +58,11 @@ def get_recurrent_dates(rrulestring, startdate, exdate=None, tz=None, context=No def todate(date): val = parser.parse(''.join((re.compile('\d')).findall(date))) - ## Dates are localized to saved timezone if any, else defaulted to - ## current timezone. WARNING: these last event dates are considered as - ## "floating" dates. + ## Dates are localized to saved timezone if any, else current timezone. if not val.tzinfo: val = pytz.UTC.localize(val) return val.astimezone(timezone) - ## Note that we haven't any context tz info when called by the server, so - ## we'll default to UTC which could induce one-day errors in date - ## calculation. timezone = pytz.timezone(tz or context.get('tz') or 'UTC') if not startdate: @@ -73,8 +75,7 @@ def get_recurrent_dates(rrulestring, startdate, exdate=None, tz=None, context=No for date in exdate: datetime_obj = todate(date) rset1._exdate.append(datetime_obj) - - print [d.astimezone(pytz.UTC) for d in rset1] + return [d.astimezone(pytz.UTC) for d in rset1] def base_calendar_id2real_id(base_calendar_id=None, with_date=False): @@ -162,7 +163,7 @@ class calendar_attendee(osv.osv): 'state': fields.selection([('needs-action', 'Needs Action'),('tentative', 'Uncertain'),('declined', 'Declined'),('accepted', 'Accepted')], 'Status', readonly=True, help="Status of the attendee's participation"), 'cn': fields.function(_compute_data, string='Common name', type="char", size=124, multi='cn', store=True), 'dir': fields.char('URI Reference', size=124, help="Reference to the URI that points to the directory information corresponding to the attendee."), - 'partner_id': fields.many2one('res.partner', 'Contact'), + 'partner_id': fields.many2one('res.partner', 'Contact',readonly="True"), 'email': fields.char('Email', size=124, help="Email of Invited Person"), 'event_date': fields.function(_compute_data, string='Event Date', type="datetime", multi='event_date'), 'event_end_date': fields.function(_compute_data, string='Event End Date', type="datetime", multi='event_end_date'), @@ -221,31 +222,27 @@ class calendar_attendee(osv.osv): if event_obj.rrule: event.add('rrule').value = event_obj.rrule - #"TO DO == replace by alarm ids" - -# if event_obj.alarm_id: -# # computes alarm data -# valarm = event.add('valarm') -# alarm_object = self.pool.get('res.alarm') -# alarm_data = alarm_object.read(cr, uid, event_obj.alarm_id.id, context=context) -# # Compute trigger data -# interval = alarm_data['trigger_interval'] -# occurs = alarm_data['trigger_occurs'] -# duration = (occurs == 'after' and alarm_data['trigger_duration']) \ -# or -(alarm_data['trigger_duration']) -# related = alarm_data['trigger_related'] -# trigger = valarm.add('TRIGGER') -# trigger.params['related'] = [related.upper()] -# if interval == 'days': -# delta = timedelta(days=duration) -# if interval == 'hours': -# delta = timedelta(hours=duration) -# if interval == 'minutes': -# delta = timedelta(minutes=duration) -# trigger.value = delta -# # Compute other details -# valarm.add('DESCRIPTION').value = alarm_data['name'] or 'OpenERP' - + if event_obj.alarm_ids: + for alarm in event_obj.alarm_ids: + # computes alarm data + valarm = event.add('valarm') + # Compute trigger data + interval = alarm.interval + occurs = 'before' + duration = alarm.duration + + trigger = valarm.add('TRIGGER') + trigger.params['related'] = ["START"] + if interval == 'days': + delta = timedelta(days=duration) + elif interval == 'hours': + delta = timedelta(hours=duration) + elif interval == 'minutes': + delta = timedelta(minutes=duration) + + trigger.value = delta + # Compute other details + valarm.add('DESCRIPTION').value = alarm.name or 'OpenERP' for attendee in event_obj.attendee_ids: attendee_add = event.add('attendee') attendee_add.value = 'MAILTO:' + (attendee.email or '') @@ -257,6 +254,8 @@ class calendar_attendee(osv.osv): Send mail for event invitation to event attendees. @param email_from: email address for user sending the mail """ + res = False + mail_id = [] data_pool = self.pool.get('ir.model.data') mail_pool = self.pool.get('mail.mail') @@ -269,7 +268,7 @@ class calendar_attendee(osv.osv): 'declined':'red' } - for attendee in self.browse(cr, uid, ids, context=context): + for attendee in self.browse(cr, uid, ids, context=context): res_obj = attendee.ref if res_obj: dummy,template_id = data_pool.get_object_reference(cr, uid, 'base_calendar', "crm_email_template_meeting_invitation") @@ -287,14 +286,30 @@ class calendar_attendee(osv.osv): vals['attachment_ids'] = [(0,0,{'name': 'invitation.ics', 'datas_fname': 'invitation.ics', 'datas': str(ics_file).encode('base64')})] - vals['auto_delete'] = True #We don't want to have the mail of everybody in the tchatter ! - + vals['model'] = None #We don't want to have the mail in the tchatter while in queue! + vals['auto_delete'] = True #We don't need mail after it has been sended ! + if not attendee.partner_id.opt_out: mail_id.append(mail_pool.create(cr, uid, vals, context=context)) + if mail_id: - return mail_pool.send(cr, uid, mail_id, context=context) - return False + try: + res = mail_pool.send(cr, uid, mail_id, context=context) + except Exception as e: + print e + + return res + + + def do_attendee_decline(self, cr, uid, ids, context=None): + return self.do_decline(cr, uid, ids, context=context) + + def do_attendee_accept(self, cr, uid, ids, context=None): + return self.do_accept(cr, uid, ids, context=context) + + def do_attendee_maybe(self, cr, uid, ids, context=None): + return self.do_tentative(cr, uid, ids, context=context) def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv): """ @@ -357,195 +372,14 @@ class calendar_attendee(osv.osv): res = super(calendar_attendee, self).create(cr, uid, vals, context=context) return res -# -# class res_alarm(osv.osv): -# """Resource Alarm """ -# _name = 'res.alarm' -# _description = 'Basic Alarm Information' -# -# _columns = { -# 'name':fields.char('Name', size=256, required=True), -# 'trigger_occurs': fields.selection([('before', 'Before'), ('after', 'After')], 'Triggers', required=True), -# 'trigger_interval': fields.selection([('minutes', 'Minutes'), ('hours', 'Hours'), ('days', 'Days')], 'Interval', required=True), -# 'trigger_duration': fields.integer('Duration', required=True), -# 'trigger_related': fields.selection([('start', 'The event starts'), ('end', 'The event ends')], 'Related to', required=True), -# 'duration': fields.integer('Duration', help="""Duration' and 'Repeat' are both optional, but if one occurs, so MUST the other"""), -# 'repeat': fields.integer('Repeat'), -# 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the event alarm information without removing it.") -# } -# _defaults = { -# 'trigger_interval': 'minutes', -# 'trigger_duration': 5, -# 'trigger_occurs': 'before', -# 'trigger_related': 'start', -# 'active': 1, -# } -# -# def do_alarm_create(self, cr, uid, ids, model, date, context=None): -# """ -# Create Alarm for event. -# @param model: Model name. -# @param date: Event date -# @param context: A standard dictionary for contextual values -# @return: True -# """ -# if context is None: -# context = {} -# alarm_obj = self.pool.get('calendar.alarm') -# res_alarm_obj = self.pool.get('res.alarm') -# ir_obj = self.pool.get('ir.model') -# model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0] -# -# model_obj = self.pool[model] -# for data in model_obj.browse(cr, uid, ids, context=context): -# -# basic_alarm = data.alarm_id -# cal_alarm = data.base_calendar_alarm_id -# if (not basic_alarm and cal_alarm) or (basic_alarm and cal_alarm): -# new_res_alarm = None -# # Find for existing res.alarm -# duration = cal_alarm.trigger_duration -# interval = cal_alarm.trigger_interval -# occurs = cal_alarm.trigger_occurs -# related = cal_alarm.trigger_related -# domain = [('trigger_duration', '=', duration), ('trigger_interval', '=', interval), ('trigger_occurs', '=', occurs), ('trigger_related', '=', related)] -# alarm_ids = res_alarm_obj.search(cr, uid, domain, context=context) -# if not alarm_ids: -# val = { -# 'trigger_duration': duration, -# 'trigger_interval': interval, -# 'trigger_occurs': occurs, -# 'trigger_related': related, -# 'name': str(duration) + ' ' + str(interval) + ' ' + str(occurs) -# } -# new_res_alarm = res_alarm_obj.create(cr, uid, val, context=context) -# else: -# new_res_alarm = alarm_ids[0] -# cr.execute('UPDATE %s ' % model_obj._table + \ -# ' SET base_calendar_alarm_id=%s, alarm_id=%s ' \ -# ' WHERE id=%s', -# (cal_alarm.id, new_res_alarm, data.id)) -# -# self.do_alarm_unlink(cr, uid, [data.id], model) -# if basic_alarm: -# vals = { -# 'action': 'display', -# 'description': data.description, -# 'name': data.name, -# 'attendee_ids': [(6, 0, map(lambda x:x.id, data.attendee_ids))], -# 'trigger_related': basic_alarm.trigger_related, -# 'trigger_duration': basic_alarm.trigger_duration, -# 'trigger_occurs': basic_alarm.trigger_occurs, -# 'trigger_interval': basic_alarm.trigger_interval, -# 'duration': basic_alarm.duration, -# 'repeat': basic_alarm.repeat, -# 'state': 'run', -# 'event_date': data[date], -# 'res_id': data.id, -# 'model_id': model_id, -# 'user_id': uid -# } -# alarm_id = alarm_obj.create(cr, uid, vals) -# cr.execute('UPDATE %s ' % model_obj._table + \ -# ' SET base_calendar_alarm_id=%s, alarm_id=%s ' -# ' WHERE id=%s', \ -# ( alarm_id, basic_alarm.id, data.id) ) -# return True -# -# def do_alarm_unlink(self, cr, uid, ids, model, context=None): -# """ -# Delete alarm specified in ids -# @param cr: the current row, from the database cursor, -# @param uid: the current user's ID for security checks, -# @param ids: List of res alarm's IDs. -# @param model: Model name for which alarm is to be cleared. -# @return: True -# """ -# if context is None: -# context = {} -# alarm_obj = self.pool.get('calendar.alarm') -# ir_obj = self.pool.get('ir.model') -# model_id = ir_obj.search(cr, uid, [('model', '=', model)])[0] -# model_obj = self.pool[model] -# for data in model_obj.browse(cr, uid, ids, context=context): -# alarm_ids = alarm_obj.search(cr, uid, [('model_id', '=', model_id), ('res_id', '=', data.id)]) -# if alarm_ids: -# alarm_obj.unlink(cr, uid, alarm_ids) -# cr.execute('Update %s set base_calendar_alarm_id=NULL, alarm_id=NULL\ -# where id=%%s' % model_obj._table,(data.id,)) -# return True -# -# class calendar_alarm(osv.osv): -# _name = 'calendar.alarm' -# _description = 'Event alarm information' -# _inherit = 'res.alarm' -# __attribute__ = {} -# -# _columns = { -# 'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'), -# 'name': fields.char('Summary', size=124, help="""Contains the text to be \ -# used as the message subject for email \ -# or contains the text to be used for display"""), -# 'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \ -# ('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \ -# required=True, help="Defines the action to be invoked when an alarm is triggered"), -# 'description': fields.text('Description', help='Provides a more complete \ -# description of the calendar component, than that \ -# provided by the "SUMMARY" property'), -# 'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \ -# 'alarm_id', 'attendee_id', 'Attendees', readonly=True), -# 'attach': fields.binary('Attachment', help="""* Points to a sound resource,\ -# which is rendered when the alarm is triggered for audio, -# * File which is intended to be sent as message attachments for email, -# * Points to a procedure resource, which is invoked when\ -# the alarm is triggered for procedure."""), -# 'res_id': fields.integer('Resource ID'), -# 'model_id': fields.many2one('ir.model', 'Model'), -# 'user_id': fields.many2one('res.users', 'Owner'), -# 'event_date': fields.datetime('Event Date'), -# 'event_end_date': fields.datetime('Event End Date'), -# 'trigger_date': fields.datetime('Trigger Date', readonly="True"), -# 'state':fields.selection([ -# ('draft', 'Draft'), -# ('run', 'Run'), -# ('stop', 'Stop'), -# ('done', 'Done'), -# ], 'Status', select=True, readonly=True), -# } -# -# _defaults = { -# 'action': 'email', -# 'state': 'run', -# } -# -# def create(self, cr, uid, vals, context=None): -# """ -# Overrides orm create method. -# @param self: The object pointer -# @param cr: the current row, from the database cursor, -# @param vals: dictionary of fields value.{'name_of_the_field': value, ...} -# @param context: A standard dictionary for contextual values -# @return: new record id for calendar_alarm. -# """ -# if context is None: -# context = {} -# event_date = vals.get('event_date', False) -# if event_date: -# dtstart = datetime.strptime(vals['event_date'], "%Y-%m-%d %H:%M:%S") -# if vals['trigger_interval'] == 'days': -# delta = timedelta(days=vals['trigger_duration']) -# if vals['trigger_interval'] == 'hours': -# delta = timedelta(hours=vals['trigger_duration']) -# if vals['trigger_interval'] == 'minutes': -# delta = timedelta(minutes=vals['trigger_duration']) -# trigger_date = dtstart + (vals['trigger_occurs'] == 'after' and delta or -delta) -# vals['trigger_date'] = trigger_date -# res = super(calendar_alarm, self).create(cr, uid, vals, context=context) -# return res class res_partner(osv.osv): _inherit = 'res.partner' + _columns = { + 'cal_last_notif': fields.datetime('Last Notification from base Calendar'), + } + def get_attendee_detail(self, cr, uid, ids, meeting_id, context=None): datas = [] meeting = False @@ -560,111 +394,411 @@ class res_partner(osv.osv): datas.append(data) return datas - def do_run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False, \ - context=None): - """Scheduler for event reminder - @param ids: List of calendar alarm's IDs. - @param use_new_cursor: False or the dbname + def update_cal_last_event(self,cr,uid,context=None): + partner = self.pool.get('res.users').browse(cr,uid,uid,context=context).partner_id; + self.write(cr,uid,partner.id,{'cal_last_notif' : datetime.now() } ,context=context) + return "OK" +# +# def do_run_scheduler(self, cr, uid, automatic=False, use_new_cursor=False, \ +# context=None): +# """Scheduler for event reminder +# @param ids: List of calendar alarm's IDs. +# @param use_new_cursor: False or the dbname +# """ +# if context is None: +# context = {} +# current_datetime = datetime.now() +# alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context) +# +# mail_to = "" +# +# for alarm in self.browse(cr, uid, alarm_ids, context=context): +# next_trigger_date = None +# update_vals = {} +# model_obj = self.pool[alarm.model_id.model] +# res_obj = model_obj.browse(cr, uid, alarm.res_id, context=context) +# re_dates = [] +# +# if hasattr(res_obj, 'rrule') and res_obj.rrule: +# recurrent_dates = get_recurrent_dates(res_obj.rrule, res_obj.date, res_obj.exdate, res_obj.vtimezone, res_obj.exrule, context=context) +# +# trigger_interval = alarm.interval +# if trigger_interval == 'days': +# delta = timedelta(days=alarm.duration) +# if trigger_interval == 'hours': +# delta = timedelta(hours=alarm.duration) +# if trigger_interval == 'minutes': +# delta = timedelta(minutes=alarm.duration) +# +# for rdate in recurrent_dates: +# if rdate + delta > current_datetime: +# break +# if rdate + delta <= current_datetime: +# re_dates.append(rdate.strftime("%Y-%m-%d %H:%M:%S")) +# rest_dates = recurrent_dates[len(re_dates):] +# next_trigger_date = rest_dates and rest_dates[0] or None +# +# else: +# re_dates = [alarm.date] +# +# if re_dates: +# if alarm.action == 'email': +# sub = '[OpenERP Reminder] %s' % (alarm.name) +# body = """
Event: %s +# Event Date: %s +# Description: %s +# From: %s +# ---- +# %s +#""" % (alarm.name, alarm.trigger_date, alarm.description, alarm.user_id.name, alarm.user_id.signature) +# mail_to = alarm.user_id.email +# for att in alarm.attendee_ids: +# mail_to = mail_to + " " + att.user_id.email +# if mail_to: +# vals = { +# 'state': 'outgoing', +# 'subject': sub, +# 'body_html': body, +# 'email_to': mail_to, +# 'email_from': tools.config.get('email_from', mail_to), +# } +# self.pool.get('mail.mail').create(cr, uid, vals, context=context) +# if next_trigger_date: +# update_vals.update({'trigger_date': next_trigger_date}) +# else: +# update_vals.update({'state': 'done'}) +# self.write(cr, uid, [alarm.id], update_vals) +# return True + +class calendar_alarm_manager(osv.osv): + _name = 'calendar.alarm_manager' + + def get_next_potential_limit_alarm(self,cr,uid,seconds, notif=True, mail=True, partner_id=None, context=None): + res = {} + print "Search for partner: ", partner_id + base_request = """ + SELECT + crm.id, + crm.date - interval '1' minute * calcul_delta.max_delta AS first_alarm, + CASE + WHEN crm.recurrency THEN crm.end_date - interval '1' minute * calcul_delta.min_delta + ELSE crm.date_deadline - interval '1' minute * calcul_delta.min_delta + END as last_alarm, + crm.date as first_event_date, + CASE + WHEN crm.recurrency THEN crm.end_date + ELSE crm.date_deadline + END as last_event_date, + calcul_delta.min_delta, + calcul_delta.max_delta, + crm.rrule + FROM + crm_meeting AS crm + RIGHT JOIN + ( + SELECT + rel.crm_meeting_id, max(alarm.duration_minutes) AS max_delta,min(alarm.duration_minutes) AS min_delta + FROM + calendar_alarm_crm_meeting_rel AS rel + LEFT JOIN calendar_alarm AS alarm ON alarm.id = rel.calendar_alarm_id + WHERE alarm.type in %s + GROUP BY rel.crm_meeting_id + ) AS calcul_delta ON calcul_delta.crm_meeting_id = crm.id + """ + + filter_user = """ + LEFT JOIN crm_meeting_res_partner_rel AS part_rel ON part_rel.crm_meeting_id = crm.id + AND part_rel.res_partner_id = %s """ + + + #Add filter on type + type_to_read = () #('dummy',) + if notif: + type_to_read += ('notification',) + if mail: + type_to_read += ('email',) + + tuple_params = (type_to_read,) + + + #ADD FILTER ON PARTNER_ID + if partner_id: + base_request += filter_user + tuple_params += (partner_id, ) + + #Add filter on hours + tuple_params += (seconds,seconds,) + + print(tuple_params) + cr.execute(""" + SELECT + * + FROM ( + """ + + base_request + + """ + ) AS ALL_EVENTS + WHERE + ALL_EVENTS.first_alarm < (now() at time zone 'utc' + interval '%s' second ) + AND ALL_EVENTS.last_alarm > (now() at time zone 'utc' - interval '%s' second ) + """,tuple_params) + + for event_id, first_alarm,last_alarm,first_meeting,last_meeting,min_duration,max_duration,rrule in cr.fetchall(): + res[event_id] = {} + res[event_id]['event_id'] = event_id + res[event_id]['first_alarm'] = first_alarm + res[event_id]['last_alarm'] = last_alarm + res[event_id]['first_meeting'] = first_meeting + res[event_id]['last_meeting'] = last_meeting + res[event_id]['min_duration'] = min_duration + res[event_id]['max_duration'] = max_duration + res[event_id]['rrule'] = rrule + + print "All event from SQL : ",res + return res + + def do_check_alarm_for_one_date(self,cr,uid,one_date,event, event_maxdelta,in_the_next_X_seconds,after=False,notif=True, mail=True,context=None): + res = [] + alarm_type = [] + + if notif: + alarm_type.append('notification') + if mail: + alarm_type.append('email') + + if one_date - timedelta(minutes=event_maxdelta) < datetime.now() + timedelta(seconds=in_the_next_X_seconds): #if an alarm is possible for this date + print "ALARMIDS = ", event.alarm_ids + for alarm in event.alarm_ids: + print "type = ",alarm.type + print "after = ",after + print "cond 0 =", alarm.type in alarm_type + print "cond 1 = ", one_date - timedelta(minutes=alarm.duration_minutes), " < ", datetime.now() + timedelta(seconds=in_the_next_X_seconds) + #print "cond 2", one_date - timedelta(minutes=alarm.duration_minutes), " > ",datetime.strptime(after.split('.')[0], "%Y-%m-%d %H:%M:%S") + + print + if alarm.type in alarm_type and \ + one_date - timedelta(minutes=alarm.duration_minutes) < datetime.now() + timedelta(seconds=in_the_next_X_seconds) and \ + (not after or one_date - timedelta(minutes=alarm.duration_minutes) > datetime.strptime(after.split('.')[0], "%Y-%m-%d %H:%M:%S")): + alert = { + 'alarm_id' : alarm.id, + 'event_id' : event.id, + 'notify_at' : one_date - timedelta(minutes=alarm.duration_minutes), + } + print "ALERT ADDED : ", alert + res.append(alert) + else: + print "Not in condition..." + + print "DATE SERVER:", datetime.now(); + return res + + def do_run_scheduler_mail(self,cr,uid,context=None): + cron = self.pool.get('ir.cron').search(cr,uid,[('model','ilike',self._name)],context=context) + if cron and len(cron) == 1: + cron = self.pool.get('ir.cron').browse(cr,uid,cron[0],context=context) + else: + raise ("Cron for " + self._name + " not identified :( !") + + if cron.interval_type=="weeks": + cron_interval = cron.interval_number * 7 * 24 * 60 * 60 + elif cron.interval_type=="days": + cron_interval = cron.interval_number * 24 * 60 * 60 + elif cron.interval_type=="hours": + cron_interval = cron.interval_number * 60 * 60 + elif cron.interval_type=="minutes": + cron_interval = cron.interval_number * 60 + elif cron.interval_type=="seconds": + cron_interval = cron.interval_number + + if not cron_interval: + raise ("Cron delay for " + self._name + " not calculated :( !") + + print "Cron interval = ",cron_interval + + all_events = self.get_next_potential_limit_alarm(cr,uid,cron_interval,notif=False,context=context) + for event in all_events: #.values() + max_delta = all_events[event]['max_duration']; + curEvent = self.pool.get('crm.meeting').browse(cr,uid,event,context=context) + if curEvent.recurrency: + bFound = False + LastFound = False + for one_date in get_recurrent_dates(curEvent.rrule, curEvent.date, curEvent.exdate, curEvent.vtimezone, context=context) : + in_date_format = datetime.strptime(one_date, '%Y-%m-%d %H:%M:%S'); + LastFound = self.do_check_alarm_for_one_date(cr,uid,in_date_format,curEvent,max_delta,cron_interval,notif=False,context=context) + if LastFound: + for alert in LastFound: + self.do_mail_reminder(cr,uid,alert,context=context) + + if not bFound: #if it's the first alarm for this recurrent event + bFound = True + if bFound and not LastFound: #if the precendent event had alarm but not this one, we can stop the search fot this event + break + else: + in_date_format = datetime.strptime(curEvent.date, '%Y-%m-%d %H:%M:%S'); + LastFound = self.do_check_alarm_for_one_date(cr,uid,in_date_format,curEvent,max_delta,cron_interval,notif=False,context=context) + if LastFound: + for alert in LastFound: + self.do_mail_reminder(cr,uid,alert,context=context) + + #Purge all done + + def get_next_event(self,cr,uid,context=None): + ajax_check_every_seconds = 300 + + partner = self.pool.get('res.users').browse(cr,uid,uid,context=context).partner_id; + print "Last alert for partner : ",partner.cal_last_notif + + all_notif = [] + + all_events = self.get_next_potential_limit_alarm(cr,uid,ajax_check_every_seconds,partner_id=partner.id,mail=False,context=context) + print all_events + + for event in all_events: #.values() + max_delta = all_events[event]['max_duration']; + curEvent = self.pool.get('crm.meeting').browse(cr,uid,event,context=context) + if curEvent.recurrency: + bFound = False + LastFound = False + for one_date in get_recurrent_dates(curEvent.rrule, curEvent.date, curEvent.exdate, curEvent.vtimezone, context=context) : + in_date_format = datetime.strptime(one_date, '%Y-%m-%d %H:%M:%S'); + LastFound = self.do_check_alarm_for_one_date(cr,uid,in_date_format,curEvent,max_delta,ajax_check_every_seconds,after=partner.cal_last_notif,mail=False,context=context) + if LastFound: + for alert in LastFound: + all_notif.append(self.do_notif_reminder(cr,uid,alert,context=context)) + if not bFound: #if it's the first alarm for this recurrent event + bFound = True + if bFound and not LastFound: #if the precendent event had alarm but not this one, we can stop the search fot this event + break + else: + in_date_format = datetime.strptime(curEvent.date, '%Y-%m-%d %H:%M:%S'); + LastFound = self.do_check_alarm_for_one_date(cr,uid,in_date_format,curEvent,max_delta,ajax_check_every_seconds,partner.cal_last_notif,mail=False,context=context) + print "Lastfound = ",LastFound + if LastFound: + for alert in LastFound: + all_notif.append(self.do_notif_reminder(cr,uid,alert,context=context)) + + return all_notif + + def do_mail_reminder(self,cr,uid,alert,context=None): if context is None: context = {} - current_datetime = datetime.now() - alarm_ids = self.search(cr, uid, [('state', '!=', 'done')], context=context) + event = self.pool.get("crm.meeting").browse(cr,uid,alert['event_id'],context=context) + alarm = self.pool.get("calendar.alarm").browse(cr,uid,alert['alarm_id'],context=context) + + if alarm.type == 'email': + mail_id = [] + mail_pool = self.pool.get('mail.mail') + data_pool = self.pool.get('ir.model.data') + template_pool = self.pool.get('email.template') + local_context = {} + + for attendee in event.attendee_ids: + dummy,template_id = data_pool.get_object_reference(cr, uid, 'base_calendar', "crm_email_template_meeting_reminder") + dummy,act_id = data_pool.get_object_reference(cr, uid, 'base_calendar', "view_crm_meeting_calendar") + body = template_pool.browse(cr, uid, template_id, context=context).body_html + if attendee.email: # and tools.config.get('email_from', False): + local_context['att_obj'] = attendee + local_context['action_id'] = self.pool.get('ir.actions.act_window').search(cr, uid, [('view_id','=',act_id)], context=context)[0] + local_context['dbname'] = cr.dbname + local_context['base_url'] = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) + vals = template_pool.generate_email(cr, uid, template_id, event.id, context=local_context) + vals['model'] = None #We don't want to have the mail in the tchatter while in queue! + vals['auto_delete'] = True #We don't need mail after it has been sended ! + + if not attendee.partner_id.opt_out: + mail_id.append(mail_pool.create(cr, uid, vals, context=context)) + + if mail_id: + for mail in mail_pool.browse(cr,uid,mail_id,context=context): + print "REMINDER SENDED ... EMAIL : ",mail.id + + else: + print "SHOULD BE AN MAIL ALARM :( FOR EVENT %s / ALARM %s" % (alert['event_id'],alert['alarm_id']) - mail_to = "" + def do_notif_reminder(self,cr,uid,alert,context=None): + alarm = self.pool.get("calendar.alarm").browse(cr,uid,alert['alarm_id'],context=context) + event = self.pool.get("crm.meeting").browse(cr,uid,alert['event_id'],context=context) + + if alarm.type == 'notification': + mail_id = [] + mail_pool = self.pool.get('mail.mail') + data_pool = self.pool.get('ir.model.data') + template_pool = self.pool.get('email.template') + local_context = context.copy() + message = event.display_time + + delta = alert['notify_at'] - datetime.now() + delta = delta.seconds + delta.days * 3600 * 24 + + return { + 'event_id' : event.id, + 'title' : event.name, + 'message' : message, + 'timer' : delta, #Now - event_date - alaram.duration_minute + 'notify_at' : alert['notify_at'].strftime("%Y-%m-%d %H:%M:%S"), #Now - event_date - alaram.duration_minute + } + + else: + print "SHOULD BE AN NOTIF ALARM :( FOR EVENT %s / ALARM %s" % (alert['event_id'],alert['alarm_id']) - for alarm in self.browse(cr, uid, alarm_ids, context=context): - next_trigger_date = None - update_vals = {} - model_obj = self.pool[alarm.model_id.model] - res_obj = model_obj.browse(cr, uid, alarm.res_id, context=context) - re_dates = [] - if hasattr(res_obj, 'rrule') and res_obj.rrule: - recurrent_dates = get_recurrent_dates(res_obj.rrule, res_obj.date, res_obj.exdate, res_obj.vtimezone, res_obj.exrule, context=context) - - trigger_interval = alarm.trigger_interval - if trigger_interval == 'days': - delta = timedelta(days=alarm.trigger_duration) - if trigger_interval == 'hours': - delta = timedelta(hours=alarm.trigger_duration) - if trigger_interval == 'minutes': - delta = timedelta(minutes=alarm.trigger_duration) - delta = alarm.trigger_occurs == 'after' and delta or -delta - - for rdate in recurrent_dates: - if rdate + delta > current_datetime: - break - if rdate + delta <= current_datetime: - re_dates.append(rdate.strftime("%Y-%m-%d %H:%M:%S")) - rest_dates = recurrent_dates[len(re_dates):] - next_trigger_date = rest_dates and rest_dates[0] or None - - else: - re_dates = [alarm.trigger_date] - - if re_dates: - if alarm.action == 'email': - sub = '[OpenERP Reminder] %s' % (alarm.name) - body = """
Event: %s - Event Date: %s - Description: %s - From: %s - ---- - %s -""" % (alarm.name, alarm.trigger_date, alarm.description, alarm.user_id.name, alarm.user_id.signature) - mail_to = alarm.user_id.email - for att in alarm.attendee_ids: - mail_to = mail_to + " " + att.user_id.email - if mail_to: - vals = { - 'state': 'outgoing', - 'subject': sub, - 'body_html': body, - 'email_to': mail_to, - 'email_from': tools.config.get('email_from', mail_to), - } - self.pool.get('mail.mail').create(cr, uid, vals, context=context) - if next_trigger_date: - update_vals.update({'trigger_date': next_trigger_date}) - else: - update_vals.update({'state': 'done'}) - self.write(cr, uid, [alarm.id], update_vals) - return True - -class calendar_friendly(osv.osv): - _name = 'calendar.friendly' - - _columns = { - 'description':fields.char('Description'), - 'user_id': fields.many2one('res.users','Me'), - 'partner_id': fields.many2one('res.partner','My friend'), - 'name':fields.char('Name'), - 'color':fields.char('Color'), - 'active':fields.boolean('active'), - } - _defaults = { - 'user_id': lambda self, cr, uid, ctx: uid, - 'active' : True, - } - +# +# def do_run_stack_scheduler(self,cr,uid,context=None): +# all_events = self.get_next_potential_limit_alarm(cr,uid,self.EVENT_ALARM_STACK_HOURS,context=context) +# for event in all_events: #.values() +# max_delta = all_events[event]['max_duration']; +# curEvent = self.pool.get('crm.meeting').browse(cr,uid,event,context=context) +# if curEvent.recurrency: +# bFound = False +# bLastFournd = False +# for one_date in get_recurrent_dates(curEvent.rrule,curEvent.date) : +# in_date_format = datetime.strptime(one_date, '%Y-%m-%d %H:%M:%S'); +# bLastFound = self.do_check_alarm_for_one_date(cr,uid,in_date_format,curEvent,max_delta,context=context) +# if bLastFound and not bFound: #if it's the first alarm for this recurrent event +# bFound = True +# if bFound and not bLastFound: #if the precendent event had alarm but not this one, we can stop the search fot this event +# break +# else: +# in_date_format = datetime.strptime(curEvent.date, '%Y-%m-%d %H:%M:%S'); +# self.do_check_alarm_for_one_date(cr,uid,in_date_format,curEvent,max_delta,context=context) +# +# #Purge all done + + class calendar_alarm(osv.osv): _name = 'calendar.alarm' _description = 'Event alarm' + def _get_duration(self, cr, uid, ids, field_name, arg, context=None): + res = {} + for alarm in self.browse(cr, uid, ids, context=context): + if alarm.interval == "minutes": + res[alarm.id] = alarm.duration + elif alarm.interval == "hours": + res[alarm.id] = alarm.duration * 60 + elif alarm.interval == "days": + res[alarm.id] = alarm.duration * 60 * 24 + else: + res[alarm.id] = 0 + return res + _columns = { 'name':fields.char('Name', size=256, required=True), # fields function 'type': fields.selection([('notification', 'Notification'), ('email', 'Email')], 'Type', required=True), 'duration': fields.integer('Amount', required=True), 'interval': fields.selection([('minutes', 'Minutes'), ('hours', 'Hours'), ('days', 'Days')], 'Unit', required=True), + + 'duration_minutes': fields.function(_get_duration, type='integer', string='duration_minutes',store=True), } + _defaults = { 'type': 'notification', 'duration': 1, 'interval': 'hours', } - + class ir_values(osv.osv): _inherit = 'ir.values' @@ -728,3 +862,931 @@ def exp_report(db, uid, object, ids, data=None, context=None): openerp.service.report.exp_report = exp_report + + + + +class ________OLD_CRM_MEETING(): + _name = 'TEMP' + + + + + + + +class crm_meeting_type(osv.Model): + _name = 'crm.meeting.type' + _description = 'Meeting Type' + _columns = { + 'name': fields.char('Name', size=64, required=True, translate=True), + } + +class crm_meeting(osv.Model): + """ Model for CRM meetings """ + _name = 'crm.meeting' + _description = "Meeting" + _order = "id desc" + _inherit = ["mail.thread", "ir.needaction_mixin"] + + def do_run_scheduler(self,cr,uid,id,context=None): + self.pool.get('calendar.alarm_manager').do_run_scheduler(cr,uid,context=context) + + def _get_recurrency_end_date(self, data, context=None): + if data.get('recurrency') and data.get('end_type') in ('count', unicode('count')): + data_date_deadline = datetime.strptime(data.get('date_deadline'), '%Y-%m-%d %H:%M:%S') + if data.get('rrule_type') in ('daily', unicode('count')): + rel_date = relativedelta(days=data.get('count')+1) + elif data.get('rrule_type') in ('weekly', unicode('weekly')): + rel_date = relativedelta(days=(data.get('count')+1)*7) + elif data.get('rrule_type') in ('monthly', unicode('monthly')): + rel_date = relativedelta(months=data.get('count')+1) + elif data.get('rrule_type') in ('yearly', unicode('yearly')): + rel_date = relativedelta(years=data.get('count')+1) + end_date = data_date_deadline + rel_date + else: + end_date = data.get('end_date') + return end_date + + def _find_my_attendee(self, cr, uid, meeting_ids, context=None): + """ + Return the first attendee where the user connected has been invited from all the meeting_ids in parameters + """ + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + for meeting_id in meeting_ids: + for attendee in self.browse(cr,uid,meeting_id,context).attendee_ids: + if user.partner_id.id == attendee.partner_id.id: + return attendee + return False + + def _get_display_time(self, cr, uid, meeting_id, context=None): + """ + Return date and time (from to from) based on duration with timezone in string : + eg. + 1) if user add duration for 2 hours, return : August-23-2013 at ( 04-30 To 06-30) (Europe/Brussels) + 2) if event all day ,return : AllDay, July-31-2013 + """ + if context is None: + context = {} + + tz = context.get('tz', False) + if not tz: #tz can have a value False, so dont do it in the default value of get ! + tz = pytz.timezone('UTC') + + meeting = self.browse(cr, uid, meeting_id, context=context) + date = fields.datetime.context_timestamp(cr, uid, datetime.strptime(meeting.date, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context) + date_deadline = fields.datetime.context_timestamp(cr, uid, datetime.strptime(meeting.date_deadline, tools.DEFAULT_SERVER_DATETIME_FORMAT), context=context) + event_date = date.strftime('%B-%d-%Y') + display_time = date.strftime('%H-%M') + if meeting.allday: + time = _("AllDay , %s") % (event_date) + elif meeting.duration < 24: + duration = date + timedelta(hours= meeting.duration) + time = ("%s at ( %s To %s) (%s)") % (event_date, display_time, duration.strftime('%H-%M'), tz) + else : + time = ("%s at %s To\n %s at %s (%s)") % (event_date, display_time, date_deadline.strftime('%B-%d-%Y'), date_deadline.strftime('%H-%M'), tz) + return time + + def _compute(self, cr, uid, ids, fields, arg, context=None): + res = {} + for meeting_id in ids: + res[meeting_id] = {} + attendee = self._find_my_attendee(cr, uid, [meeting_id], context) + for field in fields: + if field == 'is_attendee': + res[meeting_id][field] = True if attendee else False + elif field == 'attendee_status': + res[meeting_id][field] = attendee.state if attendee else 'needs-action' + elif field == 'display_time': + res[meeting_id][field] = self._get_display_time(cr, uid, meeting_id, context=context) + return res + + def _get_rulestring(self, cr, uid, ids, name, arg, context=None): + """ + Gets Recurrence rule string according to value type RECUR of iCalendar from the values given. + @return: dictionary of rrule value. + """ + + result = {} + if not isinstance(ids, list): + ids = [ids] + + for id in ids: + #read these fields as SUPERUSER because if the record is private a normal search could return False and raise an error + data = self.browse(cr, SUPERUSER_ID, id, context=context) + if data.interval < 0: + raise osv.except_osv(_('Warning!'), _('Interval cannot be negative.')) + if data.count <= 0: + raise osv.except_osv(_('Warning!'), _('Count cannot be negative or 0.')) + + data = self.read(cr, uid, id, ['id','byday','recurrency', 'month_list','end_date', 'rrule_type', 'month_by', 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', 'su', 'day', 'week_list' ], context=context) + event = data['id'] + if data['recurrency']: + result[event] = self.compute_rule_string(data) + else: + result[event] = "" + return result + + def _rrule_write(self, obj, cr, uid, ids, field_name, field_value, args, context=None): + data = self._get_empty_rrule_data() + if field_value: + data['recurrency'] = True + for event in self.browse(cr, uid, ids, context=context): + rdate = rule_date or event.date #TO CHECK :/ + update_data = self._parse_rrule(field_value, dict(data), rdate) + data.update(update_data) + self.write(cr, uid, ids, data, context=context) + return True + + def _tz_get(self, cr, uid, context=None): + return [(x.lower(), x) for x in pytz.all_timezones] + + _columns = { + 'create_date': fields.datetime('Creation Date', readonly=True), + 'write_date': fields.datetime('Write Date', readonly=True), + 'state': fields.selection([('draft', 'Unconfirmed'), ('open', 'Confirmed')], string='Status', size=16, readonly=True, track_visibility='onchange'), + + # Meeting fields + 'name': fields.char('Meeting Subject', size=128, required=True, states={'done': [('readonly', True)]}), + 'is_attendee': fields.function(_compute, string='Attendee', type="boolean", multi='attendee'), + 'attendee_status': fields.function(_compute, string='Attendee Status', type="selection", multi='attendee'), + 'display_time': fields.function(_compute, string='Event Time', type="char", multi='attendee'), + + # --------------------- + # OLD CALENDAR_EVENT + # --------------------- + 'id': fields.integer('ID', readonly=True), + 'sequence': fields.integer('Sequence'), + + 'date': fields.datetime('Date', states={'done': [('readonly', True)]}, required=True,), + 'date_deadline': fields.datetime('End Date', states={'done': [('readonly', True)]}, required=True,), + + 'duration': fields.float('Duration', states={'done': [('readonly', True)]}), + 'description': fields.text('Description', states={'done': [('readonly', True)]}), + 'class': fields.selection([('public', 'Public'), ('private', 'Private'), ('confidential', 'Public for Employees')], 'Privacy', states={'done': [('readonly', True)]}), + 'location': fields.char('Location', size=264, help="Location of Event", states={'done': [('readonly', True)]}), + 'show_as': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Show Time as', states={'done': [('readonly', True)]}), + + #'state': fields.selection([('tentative', 'Uncertain'),('cancelled', 'Cancelled'),('confirmed', 'Confirmed'),],'Status', readonly=True, track_visibility='onchange'), + + #FIELD FOR RECURRENCY + 'exdate': fields.text('Exception Date/Times', help="This property defines the list of date/time exceptions for a recurring calendar component."), + 'rrule': fields.function(_get_rulestring, type='char', size=124, fnct_inv=_rrule_write, store=True, string='Recurrent Rule'), + 'rrule_type': fields.selection([('daily', 'Day(s)'),('weekly', 'Week(s)'),('monthly', 'Month(s)'),('yearly', 'Year(s)')], 'Recurrency', states={'done': [('readonly', True)]}, help="Let the event automatically repeat at that interval"), + 'recurrency': fields.boolean('Recurrent', help="Recurrent Meeting"), + 'recurrent_id': fields.integer('Recurrent ID'), + #'recurrent_id_date': fields.datetime('Recurrent ID date'), + #'recurrence_end_date': fields.function(_get_recurrence_end_date, type='datetime', store=True, string='Recurrence end date',priority=30), + 'vtimezone': fields.selection(_tz_get, size=64, string='Timezone'), + 'end_type' : fields.selection([('count', 'Number of repetitions'), ('end_date','End date')], 'Recurrence Termination'), + 'interval': fields.integer('Repeat Every', help="Repeat every (Days/Week/Month/Year)"), + 'count': fields.integer('Repeat', help="Repeat x times"), + 'mo': fields.boolean('Mon'), + 'tu': fields.boolean('Tue'), + 'we': fields.boolean('Wed'), + 'th': fields.boolean('Thu'), + 'fr': fields.boolean('Fri'), + 'sa': fields.boolean('Sat'), + 'su': fields.boolean('Sun'), + 'month_by': fields.selection([('date', 'Date of month'),('day', 'Day of month')], 'Option'), + 'day': fields.integer('Date of month'), + 'week_list': fields.selection([('MO', 'Monday'),('TU', 'Tuesday'),('WE', 'Wednesday'),('TH', 'Thursday'),('FR', 'Friday'),('SA', 'Saturday'),('SU', 'Sunday')], 'Weekday'), + 'byday': fields.selection([('1', 'First'),('2', 'Second'),('3', 'Third'),('4', 'Fourth'),('5', 'Fifth'),('-1', 'Last')], 'By day'), + 'end_date': fields.date('Repeat Until'), + 'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}), + + 'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}), + 'color_partner_id': fields.related('user_id','partner_id','id',type="int",string="colorize",store=False), #Color of creator + 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the event alarm information without removing it."), + + 'categ_ids': fields.many2many('crm.meeting.type', 'meeting_category_rel', 'event_id', 'type_id', 'Tags'), + 'attendee_ids': fields.many2many('calendar.attendee', 'crmmeeting_attendee_rel', 'crmmeeting_id', 'attendee_id', 'Attendees'), + 'partner_ids': fields.many2many('res.partner', string='Attendees', states={'done': [('readonly', True)]}), + 'alarm_ids': fields.many2many('calendar.alarm', string='Reminders'), + } + _defaults = { + 'end_type': 'count', + 'count': 1, + 'rrule_type': False, + 'state': 'draft', + 'class': 'public', + 'show_as': 'busy', + 'month_by': 'date', + 'interval': 1, + 'active': 1, + 'user_id': lambda self, cr, uid, ctx: uid, + 'partner_ids': lambda self, cr, uid, ctx: [self.pool.get('res.users').browse(cr, uid, [uid],context=ctx)[0].partner_id.id] + } + + def _check_closing_date(self, cr, uid, ids, context=None): + for event in self.browse(cr, uid, ids, context=context): + if event.date_deadline < event.date: + return False + return True + + _constraints = [ + (_check_closing_date, 'Error ! End date cannot be set before start date.', ['date_deadline']), + ] + + def onchange_dates(self, cr, uid, ids, start_date, duration=False, end_date=False, allday=False, context=None): + """Returns duration and/or end date based on values passed + @param ids: List of calendar event's IDs. + @param start_date: Starting date + @param duration: Duration between start date and end date + @param end_date: Ending Datee + """ + if context is None: + context = {} + + value = {} + if not start_date: + return value + if not end_date and not duration: + duration = 1.00 + value['duration'] = duration + + start = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S") + if allday: # For all day event + duration = 24.0 + value['duration'] = duration + # change start_date's time to 00:00:00 in the user's timezone + user = self.pool.get('res.users').browse(cr, uid, uid) + tz = pytz.timezone(user.tz) if user.tz else pytz.utc + start = pytz.utc.localize(start).astimezone(tz) # convert start in user's timezone + start = start.replace(hour=0, minute=0, second=0) # remove time + start = start.astimezone(pytz.utc) # convert start back to utc + value['date'] = start.strftime("%Y-%m-%d %H:%M:%S") + + if end_date and not duration: + end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S") + diff = end - start + duration = float(diff.days)* 24 + (float(diff.seconds) / 3600) + value['duration'] = round(duration, 2) + elif not end_date: + end = start + timedelta(hours=duration) + value['date_deadline'] = end.strftime("%Y-%m-%d %H:%M:%S") + elif end_date and duration and not allday: + # we have both, keep them synchronized: + # set duration based on end_date (arbitrary decision: this avoid + # getting dates like 06:31:48 instead of 06:32:00) + end = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S") + diff = end - start + duration = float(diff.days)* 24 + (float(diff.seconds) / 3600) + value['duration'] = round(duration, 2) + + return {'value': value} + + def unlink_events(self, cr, uid, ids, context=None): + """ + This function deletes event which are linked with the event with recurrent_id + (Removes the events which refers to the same UID value) + """ + if context is None: + context = {} + for event_id in ids: + r_ids = self.search(cr,uid,[('recurrent_id','=',event_id)],context=context) + self.unlink(cr, uid, r_ids, context=context) + return True + + def new_invitation_token(self, cr, uid, record, partner_id): + db_uuid = self.pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') + invitation_token = hashlib.sha256('%s-%s-%s-%s-%s' % (time.time(), db_uuid, record._name, record.id, partner_id)).hexdigest() + return invitation_token + + def create_attendees(self, cr, uid, ids, context): + att_obj = self.pool.get('calendar.attendee') + user_obj = self.pool.get('res.users') + current_user = user_obj.browse(cr, uid, uid, context=context) + for event in self.browse(cr, uid, ids, context): + attendees = {} + for att in event.attendee_ids: + attendees[att.partner_id.id] = True + new_attendees = [] + new_att_partner_ids = [] #avoid to rebrowse attendees + mail_to = "" + for partner in event.partner_ids: + if partner.id in attendees: + continue + access_token = self.new_invitation_token(cr, uid, event, partner.id) + att_id = self.pool.get('calendar.attendee').create(cr, uid, { + 'partner_id': partner.id, + 'user_id': partner.user_ids and partner.user_ids[0].id or False, + 'ref': event.id, + 'access_token': access_token, + 'email': partner.email, + }, context=context) + if partner.email: + mail_to = mail_to + " " + partner.email + new_attendees.append(att_id) + new_att_partner_ids.append(partner.id) + + self.write(cr, uid, [event.id], {'attendee_ids': [(4, att) for att in new_attendees]},context=context) + + # We remove old attendees who are not in partner_ids now. + all_partner_ids = [part.id for part in event.partner_ids] + all_attendee_ids = [att.partner_id.id for att in event.attendee_ids] + partner_ids_to_remove = map(lambda x: x, set(all_attendee_ids + new_att_partner_ids) - set(all_partner_ids)) + + if partner_ids_to_remove: + attendee_ids_to_remove =self.pool.get("calendar.attendee").search(cr,uid,[('partner_id.id','in',partner_ids_to_remove),('ref.id','=',event.id)],context=context) + if attendee_ids_to_remove: + self.pool.get("calendar.attendee").unlink(cr, uid, attendee_ids_to_remove, context) + + return True + + def get_recurrent_ids(self, cr, uid, select, domain, limit=100, context=None): + """Gives virtual event ids for recurring events based on value of Recurrence Rule + This method gives ids of dates that comes between start date and end date of calendar views + + @param limit: The Number of Results to Return """ + if not context: + context = {} + + result = [] + for data in self.read(cr, uid, select, ['rrule', 'recurrency', 'exdate', 'date', 'vtimezone'], context=context): + if not data['recurrency'] or not data['rrule']: + result.append(data['id']) + continue +# event_date = datetime.strptime(data['date'], "%Y-%m-%d %H:%M:%S") +# event_date = pytz.UTC.localize(event_date) + rdates = get_recurrent_dates(data['rrule'], data['date'], data['exdate'], data['vtimezone'], context=context) + for r_date in rdates: + # fix domain evaluation + # step 1: check date and replace expression by True or False, replace other expressions by True + # step 2: evaluation of & and | + # check if there are one False + pile = [] + ok = True + for arg in domain: + if str(arg[0]) in (str('date'), str('date_deadline'), str('end_date')): + if (arg[1] == '='): + ok = r_date.strftime('%Y-%m-%d')==arg[2] + if (arg[1] == '>'): + ok = r_date.strftime('%Y-%m-%d')>arg[2] + if (arg[1] == '<'): + ok = r_date.strftime('%Y-%m-%d')
${object.organizer} invited you for the ${object.name} meeting of ${object.user_id.company_id.name}.
+|
+ ${object.get_interval(object.date, 'dayname')}
+
+ ${object.get_interval(object.date,'day')}
+
+ ${object.get_interval(object.date, 'month')}
+ ${object.get_interval(object.date, 'time')}
+ |
+
+
|
+ ||||||||||||||||