# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## 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 def get_recurrent_dates(rrulestring, startdate, exdate=None, tz=None, exrule=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. @param rrulestring: rulestring (ie: 'FREQ=DAILY;INTERVAL=1;COUNT=3') @param exdate: string of dates separated by commas (ie: '20130506220000Z,20130507220000Z') @param startdate: string start date for computing recurrent dates @param tz: pytz timezone for computing recurrent dates @param exrule: string exrule @return: list of Recurrent dates """ exdate = exdate.split(',') if exdate else [] startdate = pytz.UTC.localize( datetime.strptime(startdate, "%Y-%m-%d %H:%M:%S")) 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. 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: startdate = datetime.now() ## Convert the start date to saved timezone (or context tz) as it'll ## define the correct hour/day asked by the user to repeat for recurrence. startdate = startdate.astimezone(timezone) rset1 = rrule.rrulestr(str(rrulestring), dtstart=startdate, forceset=True) for date in exdate: datetime_obj = todate(date) rset1._exdate.append(datetime_obj) if exrule: rset1.exrule(rrule.rrulestr(str(exrule), dtstart=startdate)) return [d.astimezone(pytz.UTC) for d in rset1] def base_calendar_id2real_id(base_calendar_id=None, with_date=False): """ Convert a "virtual/recurring event id" (type string) into a real event id (type int). E.g. virtual/recurring event id is 4-20091201100000, so it will return 4. @param base_calendar_id: id of calendar @param with_date: if a value is passed to this param it will return dates based on value of withdate + base_calendar_id @return: real event id """ if base_calendar_id and isinstance(base_calendar_id, (str, unicode)): res = base_calendar_id.split('-') if len(res) >= 2: real_id = res[0] if with_date: real_date = time.strftime("%Y-%m-%d %H:%M:%S", \ time.strptime(res[1], "%Y%m%d%H%M%S")) start = datetime.strptime(real_date, "%Y-%m-%d %H:%M:%S") end = start + timedelta(hours=with_date) return (int(real_id), real_date, end.strftime("%Y-%m-%d %H:%M:%S")) return int(real_id) return base_calendar_id and int(base_calendar_id) or base_calendar_id def get_real_ids(ids): if isinstance(ids, (str, int, long)): return base_calendar_id2real_id(ids) if isinstance(ids, (list, tuple)): res = [] for id in ids: res.append(base_calendar_id2real_id(id)) return res def real_id2base_calendar_id(real_id, recurrent_date): """ Convert a real event id (type int) into a "virtual/recurring event id" (type string). E.g. real event id is 1 and recurrent_date is set to 01-12-2009 10:00:00, so it will return 1-20091201100000. @param real_id: real event id @param recurrent_date: real event recurrent date @return: string containing the real id and the recurrent date """ if real_id and recurrent_date: recurrent_date = time.strftime("%Y%m%d%H%M%S", \ time.strptime(recurrent_date, "%Y-%m-%d %H:%M:%S")) return '%d-%s' % (real_id, recurrent_date) return real_id class calendar_attendee(osv.osv): """ Calendar Attendee Information """ _name = 'calendar.attendee' _description = 'Attendee information' _rec_name = 'cutype' __attribute__ = {} def _get_address(self, name=None, email=None): """ Gives email information in ical CAL-ADDRESS type format. @param name: name for CAL-ADDRESS value @param email: email address for CAL-ADDRESS value """ if name and email: name += ':' return (name or '') + (email and ('MAILTO:' + email) or '') def _compute_data(self, cr, uid, ids, name, arg, context=None): """ Compute data on function fields for attendee values. @param ids: list of calendar attendee's IDs @param name: name of field @return: dictionary of form {id: {'field Name': value'}} """ name = name[0] result = {} for attdata in self.browse(cr, uid, ids, context=context): id = attdata.id result[id] = {} if name == 'cn': if attdata.partner_id: result[id][name] = attdata.partner_id.name or False else: result[id][name] = attdata.email or '' if name == 'event_date': if attdata.ref: result[id][name] = attdata.ref.date else: result[id][name] = False if name == 'event_end_date': if attdata.ref: result[id][name] = attdata.ref.date_deadline else: result[id][name] = False return result _columns = { 'cutype': fields.selection([('individual', 'Individual'), ('group', 'Group'), ('resource', 'Resource'), ('room', 'Room'), ('unknown', 'Unknown') ], 'Invite Type', help="Specify the type of Invitation"), 'state': fields.selection([('needs-action', 'Needs Action'),('tentative', 'Uncertain'),('declined', 'Declined'),('accepted', 'Accepted')], 'Status', readonly=True, help="Status of the attendee's participation"), 'rsvp': fields.boolean('Required Reply?', help="Indicats whether the favor of a reply is requested"), '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'), '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'), 'availability': fields.selection([('free', 'Free'), ('busy', 'Busy')], 'Free/Busy', readonly="True"), 'access_token':fields.char('Invitation Token', size=256), 'ref': fields.many2one('crm.meeting','Meeting linked'), } _defaults = { 'state': 'needs-action', 'rsvp': True, 'cutype': 'individual', } def copy(self, cr, uid, id, default=None, context=None): raise osv.except_osv(_('Warning!'), _('You cannot duplicate a calendar attendee.')) def onchange_partner_id(self, cr, uid, ids, partner_id,context=None): """ Make entry on email and availbility on change of partner_id field. @param partner_id: changed value of partner id @return: dictionary of values which put value in email and availability fields """ if not partner_id: return {'value': {'email': ''}} partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) return {'value': {'email': partner.email}} def get_ics_file(self, cr, uid, event_obj, context=None): """ Returns iCalendar file for the event invitation. @param self: the object pointer @param cr: the current row, from the database cursor @param uid: the current user's id for security checks @param event_obj: event object (browse record) @param context: a standard dictionary for contextual values @return: .ics file content """ res = None def ics_datetime(idate, short=False): if idate: #returns the datetime as UTC, because it is stored as it in the database return datetime.strptime(idate, '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.timezone('UTC')) return False try: # FIXME: why isn't this in CalDAV? import vobject except ImportError: return res cal = vobject.iCalendar() event = cal.add('vevent') if not event_obj.date_deadline or not event_obj.date: raise osv.except_osv(_('Warning!'),_("First you have to specify the date of the invitation.")) event.add('created').value = ics_datetime(time.strftime('%Y-%m-%d %H:%M:%S')) event.add('dtstart').value = ics_datetime(event_obj.date) event.add('dtend').value = ics_datetime(event_obj.date_deadline) event.add('summary').value = event_obj.name if event_obj.description: event.add('description').value = event_obj.description if event_obj.location: event.add('location').value = event_obj.location if event_obj.rrule: event.add('rrule').value = event_obj.rrule # if event_obj.organizer: # event_org = event.add('organizer') # event_org.params['CN'] = [event_obj.organizer] # event_org.value = 'MAILTO:' + (event_obj.organizer) # elif event_obj.user_id or event_obj.organizer_id: # event_org = event.add('organizer') # organizer = event_obj.organizer_id # if not organizer: # organizer = event_obj.user_id # event_org.params['CN'] = [organizer.name] # event_org.value = 'MAILTO:' + (organizer.email or organizer.name) #"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' for attendee in event_obj.attendee_ids: attendee_add = event.add('attendee') attendee_add.params['CUTYPE'] = [str(attendee.cutype)] #attendee_add.params['ROLE'] = [str(attendee.role)] attendee_add.params['RSVP'] = [str(attendee.rsvp)] attendee_add.value = 'MAILTO:' + (attendee.email or '') res = cal.serialize() return res def _send_mail(self, cr, uid, ids, mail_to, email_from=tools.config.get('email_from', False), context=None): """ Send mail for event invitation to event attendees. @param email_from: email address for user sending the mail @return: True """ mail_id = [] data_pool = self.pool.get('ir.model.data') mail_pool = self.pool.get('mail.mail') template_pool = self.pool.get('email.template') local_context = context.copy() color = { 'needs-action' : 'grey', 'accepted' :'green', 'tentative' :'#FFFF00', 'declined':'red', 'delegated':'grey' } for attendee in self.browse(cr, uid, ids, context=context): res_obj = attendee.ref if res_obj: model,template_id = data_pool.get_object_reference(cr, uid, 'base_calendar', "crm_email_template_meeting_invitation") model,act_id = data_pool.get_object_reference(cr, uid, 'base_calendar', "view_crm_meeting_calendar") action_id = self.pool.get('ir.actions.act_window').search(cr, uid, [('view_id','=',act_id)], context=context) base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url', default='http://localhost:8069', context=context) body = template_pool.browse(cr, uid, template_id, context=context).body_html if attendee.email and email_from: ics_file = self.get_ics_file(cr, uid, res_obj, context=context) local_context['att_obj'] = attendee local_context['color'] = color local_context['action_id'] = action_id[0] local_context['dbname'] = cr.dbname local_context['base_url'] = base_url vals = template_pool.generate_email(cr, uid, template_id, res_obj.id, context=local_context) if ics_file: vals['attachment_ids'] = [(0,0,{'name': 'invitation.ics', 'datas_fname': 'invitation.ics', 'datas': str(ics_file).encode('base64')})] 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 def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv): """ Make entry on email and availbility on change of user_id field. @param ids: list of calendar attendee's IDs @param user_id: changed value of User id @return: dictionary of values which put value in email and availability fields """ if not user_id: return {'value': {'email': ''}} usr_obj = self.pool.get('res.users') user = usr_obj.browse(cr, uid, user_id, *args) return {'value': {'email': user.email, 'availability':user.availability}} def do_tentative(self, cr, uid, ids, context=None, *args): """ Makes event invitation as Tentative. @param ids: list of calendar attendee's IDs @param *args: get Tupple value @param context: a standard dictionary for contextual values """ return self.write(cr, uid, ids, {'state': 'tentative'}, context) def do_accept(self, cr, uid, ids, context=None, *args): """ Marks event invitation as Accepted. @param ids: list of calendar attendee's IDs @param context: a standard dictionary for contextual values @return: True """ if context is None: context = {} meeting_obj = self.pool.get('crm.meeting') res = self.write(cr, uid, ids, {'state': 'accepted'}, context) for attandee in self.browse(cr, uid, ids, context=context): meeting_ids = meeting_obj.search(cr, uid, [('attendee_ids', '=', attandee.id)], context=context) if meeting_ids: meeting_obj.message_post(cr, uid, get_real_ids(meeting_ids), body=_(("%s has accepted invitation") % (attandee.cn)), context=context) return res def do_decline(self, cr, uid, ids, context=None, *args): """ Marks event invitation as Declined. @param ids: list of calendar attendee's IDs @param *args: get Tupple value @param context: a standard dictionary for contextual values """ if context is None: context = {} meeting_obj = self.pool.get('crm.meeting') res = self.write(cr, uid, ids, {'state': 'declined'}, context) for attandee in self.browse(cr, uid, ids, context=context): meeting_ids = meeting_obj.search(cr, uid, [('attendee_ids', '=', attandee.id)], context=context) if meeting_ids: meeting_obj.message_post(cr, uid, get_real_ids(meeting_ids), body=_(("%s has declined invitation") % (attandee.cn)), context=context) return res def create(self, cr, uid, vals, context=None): if context is None: context = {} if not vals.get("email") and vals.get("cn"): cnval = vals.get("cn").split(':') email = filter(lambda x:x.__contains__('@'), cnval) vals['email'] = email and email[0] or '' vals['cn'] = vals.get("cn") 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' def get_attendee_detail(self, cr, uid, ids, meeting_id, context=None): datas = [] meeting = False if meeting_id: meeting = self.pool.get('crm.meeting').browse(cr, uid, get_real_ids(meeting_id),context) for partner in self.browse(cr, uid, ids, context=context): data = self.name_get(cr, uid, [partner.id], context)[0] if meeting: for attendee in meeting.attendee_ids: if attendee.partner_id.id == partner.id: data = (data[0], data[1], attendee.state) 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 """ 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.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_alarm(osv.osv): _name = 'calendar.alarm' _description = 'Event alarm' _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), } _defaults = { 'type': 'notification', 'duration': 1, 'interval': 'hours', } # # def _get_date(self, cr, uid, ids, name, arg, context=None): # """ # Get Date # @param self: The object pointer # @param cr: the current row, from the database cursor, # @param uid: the current user's ID for security checks, # @param ids: List of calendar todo's IDs. # @param args: list of tuples of form [(‘name_of_the_field', ‘operator', value), ...]. # @param context: A standard dictionary for contextual values # """ # # res = {} # for event in self.browse(cr, uid, ids, context=context): # res[event.id] = event.date_start # return res # # def _set_date(self, cr, uid, id, name, value, arg, context=None): # """ # Set Date # @param self: The object pointer # @param cr: the current row, from the database cursor, # @param uid: the current user's ID for security checks, # @param id: calendar's ID. # @param value: Get Value # @param args: list of tuples of form [('name_of_the_field', 'operator', value), ...]. # @param context: A standard dictionary for contextual values # """ # # assert name == 'date' # return self.write(cr, uid, id, { 'date_start': value }, context=context) class ir_values(osv.osv): _inherit = 'ir.values' def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False): new_model = [] for data in models: if type(data) in (list, tuple): new_model.append((data[0], base_calendar_id2real_id(data[1]))) else: new_model.append(data) return super(ir_values, self).set(cr, uid, key, key2, name, new_model, \ value, replace, isobject, meta, preserve_user, company) def get(self, cr, uid, key, key2, models, meta=False, context=None, res_id_req=False, without_user=True, key2_req=True): if context is None: context = {} new_model = [] for data in models: if type(data) in (list, tuple): new_model.append((data[0], base_calendar_id2real_id(data[1]))) else: new_model.append(data) return super(ir_values, self).get(cr, uid, key, key2, new_model, \ meta, context, res_id_req, without_user, key2_req) class ir_model(osv.osv): _inherit = 'ir.model' def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'): new_ids = isinstance(ids, (str, int, long)) and [ids] or ids if context is None: context = {} data = super(ir_model, self).read(cr, uid, new_ids, fields=fields, \ context=context, load=load) if data: for val in data: val['id'] = base_calendar_id2real_id(val['id']) return isinstance(ids, (str, int, long)) and data[0] or data original_exp_report = openerp.service.report.exp_report def exp_report(db, uid, object, ids, data=None, context=None): """ Export Report """ if object == 'printscreen.list': original_exp_report(db, uid, object, ids, data, context) new_ids = [] for id in ids: new_ids.append(base_calendar_id2real_id(id)) if data.get('id', False): data['id'] = base_calendar_id2real_id(data['id']) return original_exp_report(db, uid, object, new_ids, data, context) openerp.service.report.exp_report = exp_report