[IMP] slaughtering + Merge calendar_event to crm_meeting + debug + ...

bzr revid: jke@openerp.com-20131108160549-2q6snfpxvbq18l0h
This commit is contained in:
jke-openerp 2013-11-08 17:05:49 +01:00
parent b92220ea7d
commit d1c3269349
9 changed files with 1210 additions and 1496 deletions

View File

@ -42,9 +42,9 @@ 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',
#'base_calendar_view.xml',
'crm_meeting_view.xml',
'base_calendar_data.xml',
#'base_calendar_data.xml',
'crm_meeting_data.xml',
],
'js': [

File diff suppressed because it is too large Load Diff

View File

@ -7,15 +7,6 @@
<field name="object">calendar.event</field>
</record>
<record model="res.alarm" id="alarm1">
<field name="name">1 minute before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="1" />
<field name="trigger_interval">minutes</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm2">
<field name="name">5 minutes before</field>
<field name="active" eval="1" />
@ -100,20 +91,10 @@
</record>
<record model="res.alarm" id="alarm11">
<field name="name">5 hours before</field>
<field name="name">24 hours before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="5" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>
</record>
<record model="res.alarm" id="alarm12">
<field name="name">18 hours before</field>
<field name="active" eval="1" />
<field name="trigger_duration" eval="18" />
<field name="trigger_duration" eval="24" />
<field name="trigger_interval">hours</field>
<field name="trigger_occurs">before</field>
<field name="trigger_related">start</field>

View File

@ -6,7 +6,8 @@
<menuitem id="base.menu_calendar_configuration" name="Calendar"
parent="base.menu_base_config" sequence="50" groups="base.group_no_one"/>
<!-- Alarm form view -->
<!--
Alarm form view
<record id="res_alarm_form_view" model="ir.ui.view">
<field name="name">res.alarm.form</field>
<field name="model">res.alarm</field>
@ -24,8 +25,10 @@
</form>
</field>
</record>
-->
<!-- Alarm list view -->
<!-- Alarm list view
<record id="res_alarm_tree_view" model="ir.ui.view">
<field name="name">res.alarm.tree</field>
<field name="model">res.alarm</field>
@ -53,13 +56,15 @@
assigned to calendar events or meetings.
</p>
</field>
</record>
</record>
-->
<!-- Alarms menu -->
<!-- Alarms menu
<menuitem id="menu_crm_meeting_avail_alarm"
groups="base.group_no_one"
action="base_calendar.action_res_alarm_view"
parent="base.menu_calendar_configuration" sequence="5"/>
-->
<!-- Event form view -->
<record model="ir.ui.view" id="event_form_view">
@ -76,7 +81,7 @@
</header>
<sheet>
<group col="6">
<field name="name" string="Summary"
<field name="name" string="Summary USEDDDDDDDDDDDDDDDDDDDDD"
colspan="4" required="1"/>
<field name="allday" colspan="2" on_change="onchange_dates(date,False,False,allday)"/>
<newline/>
@ -87,8 +92,7 @@
<field name="date_deadline" string="End Date" required="1"
on_change="onchange_dates(date,False,date_deadline)"/>
<field name="location"/>
<field name="alarm_id" string="Reminder"
widget="selection"/>
<field name="alarm_ids" string="Reminder" />
<group colspan="2" col="4" attrs="{'readonly': [('state','=','done')]}">
<field name="recurrency"/>
</group>
@ -97,7 +101,6 @@
<page string="Event">
<group col="6" colspan="4">
<separator colspan="6" string="Visibility"/>
<field name="user_id" string="Responsible User"/>
<field name="show_as" string="Show Time as"/>
<field name="class" string="Privacy"/>
<field name="recurrent_id_date" invisible="1"/>
@ -110,10 +113,7 @@
<field name="attendee_ids" colspan="4"
nolabel="1" widget="one2many" mode="tree">
<tree string="Invitation details" editable="top">
<field name="sent_by_uid" string="From"/>
<field name="user_id" string="To"/>
<field name="email"/>
<field name="role" width="200"/>
<field name="state"/>
<button name="do_tentative"
states="needs-action,declined,accepted"
@ -132,8 +132,7 @@
<group col="4">
<field name="email"/>
<field name="rsvp"/>
<field name="cutype"/>
<field name="role"/>
<field name="cutype"/>
</group>
<group col="4">
<field name="state"/>

View File

@ -27,8 +27,11 @@ from openerp.tools.translate import _
from base_calendar import get_real_ids, base_calendar_id2real_id
from datetime import datetime, timedelta, date
import pytz
from openerp import tools
from openerp import tools, SUPERUSER_ID
import openerp
import hashlib
import ipdb;
#
# crm.meeting is defined here so that it may be used by modules other than crm,
@ -47,8 +50,47 @@ class crm_meeting(osv.Model):
_name = 'crm.meeting'
_description = "Meeting"
_order = "id desc"
_inherit = ["calendar.event", "mail.thread", "ir.needaction_mixin"]
_inherit = ["mail.thread", "ir.needaction_mixin"]
def _get_recurrence_end_date(self, cr, uid, ids, name, arg, context=None):
"""Get a good estimate of the end of the timespan concerned by an event.
This means we need to concider the last event of a recurrency, and that we
add its duration. For simple events (no rrule), the date_deadline is sufficient.
This value is stored in database and will help select events that should be
concidered candidate for display when filters are made upon dates (typically
the agenda filter will make one-month, one-week, one-day timespan searches).
"""
if not context:
context = {}
events = self.read(cr, uid, ids, ['rrule', 'exdate', 'exrule', 'duration', 'date_deadline', 'date', 'vtimezone'], context=context)
result = {}
for event in events:
duration = timedelta(hours=event['duration'])
if event['rrule']:
all_dates = get_recurrent_dates(
event['rrule'], event['date'], event['exdate'], event['vtimezone'],
event['exrule'], context=context)
if not event['vtimezone'] and not context.get('tz'):
## We are called by the server probably at update time (no
## context), and no vtimezone was recorded, so we have no
## idea of possible client timezone so we have a possible
## one-day-of error when applying RRULEs on floating dates.
## Let's add a day.
duration += timedelta(days=1)
result[event['id']] = (all_dates[-1] + duration).astimezone(pytz.UTC).strftime("%Y-%m-%d %H:%M:%S") \
if all_dates else None
else:
result[event['id']] = event['date_deadline']
return result
def _find_user_attendee(self, cr, uid, meeting_ids, context=None):
attendee_pool = self.pool.get('calendar.attendee')
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
@ -57,22 +99,7 @@ class crm_meeting(osv.Model):
if user.partner_id.id == attendee.partner_id.id:
return attendee
return False
def _compute(self, cr, uid, ids, fields, arg, context=None):
res = {}
for meeting_id in ids:
res[meeting_id] = {}
attendee = self._find_user_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 == 'event_time':
res[meeting_id][field] = self._compute_time(cr, uid, meeting_id, context=context)
return res
def _compute_time(self, cr, uid, meeting_id, context=None):
"""
Return date and time (from to from) based on duration with timezone in string :
@ -96,41 +123,463 @@ class crm_meeting(osv.Model):
else :
time = ("%s at %s To\n %s at %s (%s)") % (event_date, event_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_user_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 == 'event_time':
res[meeting_id][field] = self._compute_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.browse(cr, uid, id, context=context)
if data.recurrency:
result[data.id] = self.compute_rule_string(data)
else:
result[data.id] = ""
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
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),
'date_open': fields.datetime('Confirmed', readonly=True),
'date_closed': fields.datetime('Closed', readonly=True),
'partner_ids': fields.many2many('res.partner', 'crm_meeting_partner_rel', 'meeting_id', 'partner_id',
string='Attendees', states={'done': [('readonly', True)]}),
'state': fields.selection(
[('draft', 'Unconfirmed'), ('open', 'Confirmed')],
string='Status', size=16, readonly=True, track_visibility='onchange'),
'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)]}),
'categ_ids': fields.many2many('crm.meeting.type', 'meeting_category_rel',
'event_id', 'type_id', 'Tags'),
'attendee_ids': fields.many2many('calendar.attendee', 'meeting_attendee_rel',\
'event_id', 'attendee_id', 'Invited People', 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'),
'is_attendee': fields.function(_compute, string='Attendee', type="boolean", multi='attendee'),
'attendee_status': fields.function(_compute, string='Attendee Status', type="selection", multi='attendee'),
'event_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),
#FIELD FOR RECURRENCY
'exdate': fields.text('Exception Date/Times', help="This property defines the list of date/time exceptions for a recurring calendar component."),
'exrule': fields.char('Exception Rule', size=352, help="Defines a rule or repeating pattern of time to exclude from the recurring rule."),
'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'),
'select1': 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'),
'user_id': fields.many2one('res.users', 'Responsible', states={'done': [('readonly', True)]}),
'allday': fields.boolean('All Day', states={'done': [('readonly', True)]}),
'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 = {
'state': 'open',
'end_type': 'count',
'count': 1,
'rrule_type': False,
'state': 'tentative',
'class': 'public',
'show_as': 'busy',
'select1': 'date',
'interval': 1,
'active': 1,
'user_id': lambda self, cr, uid, ctx: uid
}
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
def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
_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={}
if context.get('mymeetings',False):
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context).partner_id.id
args += ['|', ('partner_ids', 'in', [partner_id]), ('user_id', '=', uid)]
return super(crm_meeting, self).search(cr, uid, args, offset=offset, limit=limit, order=order, context=context, count=count)
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) # change start's time to 00:00:00
start = start.astimezone(pytz.utc) # convert start back to utc
start_date = start.strftime("%Y-%m-%d %H:%M:%S")
value['date'] = start_date
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:
cr.execute("select id from %s where recurrent_id=%%s" % (self._table), (event_id,))
r_ids = map(lambda x: x[0], cr.fetchall())
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 = []
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
self.write(cr, uid, [event.id], {
'attendee_ids': [(4, att_id)]
}, context=context)
new_attendees.append(att_id)
if mail_to and current_user.email:
is_sent_mail = att_obj._send_mail(cr, uid, new_attendees, mail_to,
email_from = current_user.email, context=context)
if is_sent_mail:
self.message_post(cr, uid, event.id, body=_("An invitation email has been sent to attendee(s)"), context=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', 'exrule', 'date'], context=context):
for data in self.read(cr, uid, select, ['rrule', 'recurrency', 'exdate', 'exrule', '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)
# TOCHECK: the start date should be replaced by event date; the event date will be changed by that of calendar code
# if not data['rrule']:
# continue
#
# exdate = data['exdate'] and data['exdate'].split(',') or []
# rrule_str = data['rrule']
# new_rrule_str = []
# rrule_until_date = False
# is_until = False
# for rule in rrule_str.split(';'):
# name, value = rule.split('=')
# if name == "UNTIL":
# is_until = True
# value = parser.parse(value)
# rrule_until_date = parser.parse(value.strftime("%Y-%m-%d %H:%M:%S"))
# value = value.strftime("%Y%m%d%H%M%S")
# new_rule = '%s=%s' % (name, value)
# new_rrule_str.append(new_rule)
# new_rrule_str = ';'.join(new_rrule_str)
# rdates = get_recurrent_dates(str(new_rrule_str), exdate, event_date, data['exrule'])
# rdates = get_recurrent_dates(data['rrule'], exdate, event_date, data['exrule'])
rdates = get_recurrent_dates(data['rrule'], data['date'], data['exdate'], data['vtimezone'], data['exrule'], 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')):
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')<arg[2]
if (arg[1] == '>='):
ok = r_date.strftime('%Y-%m-%d')>=arg[2]
if (arg[1] == '<='):
ok = r_date.strftime('%Y-%m-%d')<=arg[2]
pile.append(ok)
elif str(arg) == str('&') or str(arg) == str('|'):
pile.append(arg)
else:
pile.append(True)
pile.reverse()
new_pile = []
for item in pile:
if not isinstance(item, basestring):
res = item
elif str(item) == str('&'):
first = new_pile.pop()
second = new_pile.pop()
res = first and second
elif str(item) == str('|'):
first = new_pile.pop()
second = new_pile.pop()
res = first or second
new_pile.append(res)
if [True for item in new_pile if not item]:
continue
idval = real_id2base_calendar_id(data['id'], r_date.strftime("%Y-%m-%d %H:%M:%S"))
result.append(idval)
if isinstance(select, (str, int, long)):
return ids and ids[0] or False
else:
ids = list(set(result))
return ids
def compute_rule_string(self, data):
"""
Compute rule string according to value type RECUR of iCalendar from the values given.
@param self: the object pointer
@param data: dictionary of freq and interval value
@return: string containing recurring rule (empty if no rule)
"""
def get_week_string(freq, data):
weekdays = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
if freq == 'weekly':
byday = map(lambda x: x.upper(), filter(lambda x: data.get(x) and x in weekdays, data))
if byday:
return ';BYDAY=' + ','.join(byday)
return ''
def get_month_string(freq, data):
if freq == 'monthly':
if data.get('select1')=='date' and (data.get('day') < 1 or data.get('day') > 31):
raise osv.except_osv(_('Error!'), ("Please select a proper day of the month."))
if data.get('select1')=='day':
return ';BYDAY=' + data.get('byday') + data.get('week_list')
elif data.get('select1')=='date':
return ';BYMONTHDAY=' + str(data.get('day'))
return ''
def get_end_date(data):
if data.get('end_date'):
data['end_date_new'] = ''.join((re.compile('\d')).findall(data.get('end_date'))) + 'T235959Z'
return (data.get('end_type') == 'count' and (';COUNT=' + str(data.get('count'))) or '') +\
((data.get('end_date_new') and data.get('end_type') == 'end_date' and (';UNTIL=' + data.get('end_date_new'))) or '')
freq = data.get('rrule_type', False)
res = ''
if freq:
interval_srting = data.get('interval') and (';INTERVAL=' + str(data.get('interval'))) or ''
res = 'FREQ=' + freq.upper() + get_week_string(freq, data) + interval_srting + get_end_date(data) + get_month_string(freq, data)
return res
def _get_empty_rrule_data(self):
return {
'byday' : False,
'recurrency' : False,
'end_date' : False,
'rrule_type' : False,
'select1' : False,
'interval' : 0,
'count' : False,
'end_type' : False,
'mo' : False,
'tu' : False,
'we' : False,
'th' : False,
'fr' : False,
'sa' : False,
'su' : False,
'exrule' : False,
'day' : False,
'week_list' : False
}
def _parse_rrule(self, rule, data, date_start):
day_list = ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su']
rrule_type = ['yearly', 'monthly', 'weekly', 'daily']
r = rrule.rrulestr(rule, dtstart=datetime.strptime(date_start, "%Y-%m-%d %H:%M:%S"))
if r._freq > 0 and r._freq < 4:
data['rrule_type'] = rrule_type[r._freq]
data['count'] = r._count
data['interval'] = r._interval
data['end_date'] = r._until and r._until.strftime("%Y-%m-%d %H:%M:%S")
#repeat weekly
if r._byweekday:
for i in xrange(0,7):
if i in r._byweekday:
data[day_list[i]] = True
data['rrule_type'] = 'weekly'
#repeat monthly by nweekday ((weekday, weeknumber), )
if r._bynweekday:
data['week_list'] = day_list[r._bynweekday[0][0]].upper()
data['byday'] = r._bynweekday[0][1]
data['select1'] = 'day'
data['rrule_type'] = 'monthly'
if r._bymonthday:
data['day'] = r._bymonthday[0]
data['select1'] = 'date'
data['rrule_type'] = 'monthly'
#repeat yearly but for openerp it's monthly, take same information as monthly but interval is 12 times
if r._bymonth:
data['interval'] = data['interval'] * 12
#FIXEME handle forever case
#end of recurrence
#in case of repeat for ever that we do not support right now
if not (data.get('count') or data.get('end_date')):
data['count'] = 100
if data.get('count'):
data['end_type'] = 'count'
else:
data['end_type'] = 'end_date'
return data
def _get_data(self, cr, uid, id, context=None):
return self.read(cr, uid, id,['date', 'date_deadline'])
def need_to_update(self, event_id, vals):
split_id = str(event_id).split("-")
if len(split_id) < 2:
return False
else:
date_start = vals.get('date', '')
try:
date_start = datetime.strptime(date_start, '%Y-%m-%d %H:%M:%S').strftime("%Y%m%d%H%M%S")
return date_start == split_id[1]
except Exception:
return True
def message_get_subscription_data(self, cr, uid, ids, user_pid=None, context=None):
res = {}
for virtual_id in ids:
@ -138,18 +587,7 @@ class crm_meeting(osv.Model):
result = super(crm_meeting, self).message_get_subscription_data(cr, uid, [real_id], user_pid=None, context=context)
res[virtual_id] = result[real_id]
return res
def copy(self, cr, uid, id, default=None, context=None):
default = default or {}
default['attendee_ids'] = False
return super(crm_meeting, self).copy(cr, uid, id, default, context)
def write(self, cr, uid, ids, values, context=None):
""" Override to add case management: open/close dates """
if values.get('state')and values.get('state') == 'open':
values['date_open'] = fields.datetime.now()
return super(crm_meeting, self).write(cr, uid, ids, values, context=context)
def onchange_partner_ids(self, cr, uid, ids, value, context=None):
""" The basic purpose of this method is to check that destination partners
effectively have email addresses. Otherwise a warning is thrown.
@ -162,6 +600,7 @@ class crm_meeting(osv.Model):
return res
def check_partners_email(self, cr, uid, partner_ids, context=None):
##TODO : REFACTOR !
""" Verify that selected partner_ids have an email_address defined.
Otherwise throw a warning. """
partner_wo_email_lst = []
@ -183,6 +622,7 @@ class crm_meeting(osv.Model):
# ----------------------------------------
# shows events of the day for this user
def _needaction_domain_get(self, cr, uid, context=None):
return [('date', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('date_deadline', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT + ' 23:59:59')), ('user_id', '=', uid)]
@ -231,6 +671,311 @@ class crm_meeting(osv.Model):
elif interval == 'time':
res = date.strftime('%I:%M %p')
return res
def _set_recurrency_end_date(self, data, context=None):
end_date = data.get('end_date')
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
return end_date
def search(self, cr, uid, args, offset=0, limit=0, order=None, context=None, count=False):
print 'IN SEARCH',args
if context is None:
context={}
if context.get('mymeetings',False):
partner_id = self.pool.get('res.users').browse(cr, uid, uid, context).partner_id.id
args += ['|', ('partner_ids', 'in', [partner_id]), ('user_id', '=', uid)]
new_args = []
for arg in args:
new_arg = arg
if arg[0] in ('date_deadline', unicode('date_deadline')):
if context.get('virtual_id', True):
# new_args += ['|','&',('recurrency','=',1),('end_date', arg[1], arg[2])]
new_args += ['|','&',('recurrency','=',1),('recurrence_end_date', arg[1], arg[2])]
elif arg[0] == "id":
new_id = get_real_ids(arg[2])
new_arg = (arg[0], arg[1], new_id)
new_args.append(new_arg)
#offset, limit and count must be treated separately as we may need to deal with virtual ids
res = super(crm_meeting,self).search(cr, uid, new_args, offset=0, limit=0, order=order, context=context, count=False)
if context.get('virtual_id', True):
res = self.get_recurrent_ids(cr, uid, res, args, limit, context=context)
if count:
return len(res)
elif limit:
return res[offset:offset+limit]
return res
def copy(self, cr, uid, id, default=None, context=None):
if context is None:
context = {}
default = default or {}
default['attendee_ids'] = False
res = super(crm_meeting, self).copy(cr, uid, base_calendar_id2real_id(id), default, context)
#alarm_obj = self.pool.get('res.alarm')
#alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date', context=context)
return res
def write(self, cr, uid, ids, values, context=None):
""" Override to add case management: open/close dates """
def _only_changes_to_apply_on_real_ids(field_names):
''' return True if changes are only to be made on the real ids'''
for field in field_names:
if field not in ['message_follower_ids']:
return False
return True
context = context or {}
if isinstance(ids, (str, int, long)):
ids = [ids]
res = False
if values.get('state')and values.get('state') == 'open':
values['date_open'] = fields.datetime.now()
# Special write of complex IDS
for event_id in ids[:]:
if len(str(event_id).split('-')) == 1:
continue
ids.remove(event_id)
real_event_id = base_calendar_id2real_id(event_id)
# if we are setting the recurrency flag to False or if we are only changing fields that
# should be only updated on the real ID and not on the virtual (like message_follower_ids):
# then set real ids to be updated.
if not vals.get('recurrency', True) or _only_changes_to_apply_on_real_ids(vals.keys()):
ids.append(real_event_id)
continue
#if edit one instance of a reccurrent id
data = self.read(cr, uid, event_id, ['date', 'date_deadline', \
'rrule', 'duration', 'exdate'])
if data.get('rrule'):
data.update(
vals,
recurrent_id=real_event_id,
recurrent_id_date=data.get('date'),
rrule_type=False,
rrule='',
recurrency=False,
)
#do not copy the id
if data.get('id'):
del(data['id'])
new_id = self.copy(cr, uid, real_event_id, default=data, context=context)
date_new = event_id.split('-')[1]
date_new = time.strftime("%Y%m%dT%H%M%SZ", \
time.strptime(date_new, "%Y%m%d%H%M%S"))
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
res = super(crm_meeting, self).write(cr, uid, [real_event_id], {'exdate': exdate})
context.update({'active_id': new_id, 'active_ids': [new_id]})
continue
if values.get('vtimezone', '') and values.get('vtimezone', '').startswith('/freeassociation.sourceforge.net/tzfile/'):
values['vtimezone'] = values['vtimezone'][40:]
res = super(crm_meeting, self).write(cr, uid, ids, values, context=context)
# set end_date for calendar searching
if values.get('recurrency', True) and values.get('end_type', 'count') in ('count', unicode('count')) and \
(values.get('rrule_type') or values.get('count') or values.get('date') or values.get('date_deadline')):
for data in self.read(cr, uid, ids, ['date', 'date_deadline', 'recurrency', 'rrule_type', 'count', 'end_type'], context=context):
end_date = self._set_recurrency_end_date(data, context=context)
super(crm_meeting, self).write(cr, uid, [data['id']], {'end_date': end_date}, context=context)
if values.get('partner_ids', False):
self.create_attendees(cr, uid, ids, context)
# if ('alarm_id' in vals or 'base_calendar_alarm_id' in vals)\
# or ('date' in vals or 'duration' in vals or 'date_deadline' in vals):
# alarm_obj = self.pool.get('res.alarm')
# alarm_obj.do_alarm_create(cr, uid, ids, self._name, 'date', context=context)
return res or True and False
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if vals.get('vtimezone', '') and vals.get('vtimezone', '').startswith('/freeassociation.sourceforge.net/tzfile/'):
vals['vtimezone'] = vals['vtimezone'][40:]
vals['end_date'] = self._set_recurrency_end_date(vals, context=context)
res = super(crm_meeting, self).create(cr, uid, vals, context)
#alarm_obj = self.pool.get('res.alarm')
#alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date', context=context)
self.create_attendees(cr, uid, [res], context)
return res
def do_tentative(self, cr, uid, ids, context=None, *args):
""" Makes event invitation as Tentative
@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 Event 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_cancel(self, cr, uid, ids, context=None, *args):
""" Makes event invitation as Tentative
@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 Event IDs
@param *args: Get Tupple value
@param context: A standard dictionary for contextual values
"""
return self.write(cr, uid, ids, {'state': 'cancelled'}, context)
def do_confirm(self, cr, uid, ids, context=None, *args):
""" Makes event invitation as Tentative
@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 Event IDs
@param *args: Get Tupple value
@param context: A standard dictionary for contextual values
"""
return self.write(cr, uid, ids, {'state': 'confirmed'}, context)
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
print 'IN READ_GROUP',args
ipdb.set_trace()
if not context:
context = {}
if 'date' in groupby:
raise osv.except_osv(_('Warning!'), _('Group by date is not supported, use the calendar view instead.'))
virtual_id = context.get('virtual_id', True)
context.update({'virtual_id': False})
res = super(crm_meeting, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
for re in res:
#remove the count, since the value is not consistent with the result of the search when expand the group
for groupname in groupby:
if re.get(groupname + "_count"):
del re[groupname + "_count"]
re.get('__context', {}).update({'virtual_id' : virtual_id})
return res
def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
print 'IN READ'
if context is None:
context = {}
fields2 = fields and fields[:] or None
EXTRAFIELDS = ('class','user_id','duration', 'date',
'rrule', 'vtimezone', 'exrule', 'exdate')
for f in EXTRAFIELDS:
if fields and (f not in fields):
fields2.append(f)
# FIXME This whole id mangling has to go!
if isinstance(ids, (str, int, long)):
select = [ids]
else:
select = ids
select = map(lambda x: (x, base_calendar_id2real_id(x)), select)
result = []
real_data = super(crm_meeting, self).read(cr, uid,
[real_id for base_calendar_id, real_id in select],
fields=fields2, context=context, load=load)
real_data = dict(zip([x['id'] for x in real_data], real_data))
for base_calendar_id, real_id in select:
res = real_data[real_id].copy()
ls = base_calendar_id2real_id(base_calendar_id, with_date=res and res.get('duration', 0) or 0)
if not isinstance(ls, (str, int, long)) and len(ls) >= 2:
recurrent_dates = [
d.strftime("%Y-%m-%d %H:%M:%S")
for d in get_recurrent_dates(
res['rrule'], res['date'], res['exdate'],
res['vtimezone'], res['exrule'], context=context)]
if ls[1] not in recurrent_dates:
raise KeyError(
'Virtual id %r is not valid, event %r can '
'not produce it.' % (base_calendar_id, real_id))
res['date'] = ls[1]
res['date_deadline'] = ls[2]
res['id'] = base_calendar_id
result.append(res)
for r in result:
if r['user_id']:
user_id = type(r['user_id']) in (tuple,list) and r['user_id'][0] or r['user_id']
if user_id==uid:
continue
if r['class']=='private':
for f in r.keys():
if f not in ('id','date','date_deadline','duration','user_id','state','interval','count'):
if isinstance(r[f], list):
r[f] = []
else:
r[f] = False
if f=='name':
r[f] = _('Busy')
for r in result:
for k in EXTRAFIELDS:
if (k in r) and (fields and (k not in fields)):
del r[k]
if isinstance(ids, (str, int, long)):
return result and result[0] or False
return result
def unlink(self, cr, uid, ids, context=None):
if not isinstance(ids, list):
ids = [ids]
res = False
attendee_obj=self.pool.get('calendar.attendee')
for event_id in ids[:]:
if len(str(event_id).split('-')) == 1:
continue
real_event_id = base_calendar_id2real_id(event_id)
data = self.read(cr, uid, real_event_id, ['exdate'], context=context)
date_new = event_id.split('-')[1]
date_new = time.strftime("%Y%m%dT%H%M%S", \
time.strptime(date_new, "%Y%m%d%H%M%S"))
exdate = (data['exdate'] and (data['exdate'] + ',') or '') + date_new
self.write(cr, uid, [real_event_id], {'exdate': exdate}, context=context)
ids.remove(event_id)
for event in self.browse(cr, uid, ids, context=context):
if event.attendee_ids:
attendee_obj.unlink(cr, uid, [x.id for x in event.attendee_ids], context=context)
res = super(crm_meeting, self).unlink(cr, uid, ids, context=context)
#self.pool.get('res.alarm').do_alarm_unlink(cr, uid, ids, self._name)
self.unlink_events(cr, uid, ids, context=context)
return res
class mail_message(osv.osv):
_inherit = "mail.message"
@ -250,6 +995,7 @@ class mail_message(osv.osv):
doc_dict.setdefault(virtual_id, doc_dict[get_real_ids(virtual_id)])
return super(mail_message, self)._find_allowed_model_wise(cr, uid, doc_model, doc_dict, context=context)
class ir_attachment(osv.osv):
_inherit = "ir.attachment"

View File

@ -57,136 +57,115 @@
</h2>
</div>
<notebook>
<page string="Meeting Details">
<group>
<page string="Meeting Details">
<group>
<field name="date" string="Starting at"
on_change="onchange_dates(date, duration, False, allday)"/>
<label for="duration"/>
<div>
<field name="duration" widget="float_time"
on_change="onchange_dates(date,duration,False,allday)"
class="oe_inline" attrs="{'invisible': [('allday','=',True)]}"/>
<label string="hours" attrs="{'invisible': [('allday','=',True)]}"/>
(<field name="allday" on_change="onchange_dates(date,False,False,allday)" class="oe_inline"/>
<label for="allday" string="All Day?"/>)
</div>
<field name="date_deadline" groups="base.group_no_one"
attrs="{'invisible': ['|', ('allday','=',True), ('duration','&lt;', 24)]}"
on_change="onchange_dates(date,False,date_deadline)"/>
</group>
<group>
<field name="user_id" groups="base.group_no_one" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads']}"/>
<field name="categ_ids" widget="many2many_tags"/>
<field name="location"/>
<field name="organizer" groups="base.group_no_one"/>
</group>
</group>
<label for="description"/>
<field name="description"/>
</page>
<page string="Options">
<group>
<group col="1">
<group>
<field name="recurrency"/>
</group>
<group attrs="{'invisible': [('recurrency','=',False)]}">
<label for="interval"/>
<field name="date" string="Starting at" on_change="onchange_dates(date, duration, False, allday)"/>
<label for="duration"/>
<div>
<field name="interval" attrs="{'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="rrule_type" attrs="{'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="duration" widget="float_time"
on_change="onchange_dates(date,duration,False,allday)"
class="oe_inline" attrs="{'invisible': [('allday','=',True)]}"/>
<label string="hours" attrs="{'invisible': [('allday','=',True)]}"/>
(<field name="allday" on_change="onchange_dates(date,False,False,allday)" class="oe_inline"/>
<label for="allday" string="All Day?"/>)
</div>
<label string="Until" for="end_type"/>
<div>
<field name="end_type" attrs="{'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="count" attrs="{'invisible': [('end_type', '!=', 'count')], 'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="end_date" attrs="{'invisible': [('end_type', '!=', 'end_date')], 'required': [('end_type', '=', 'end_date')]}" class="oe_inline"/>
</div>
<label string="Select Weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}"/>
<group col="2" colspan="1" name="weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}">
<field name="mo"/>
<field name="tu"/>
<field name="we"/>
<field name="th"/>
<field name="fr"/>
<field name="sa"/>
<field name="su"/>
</group>
<label string="Day of Month"
attrs="{'invisible': [('rrule_type','!=','monthly')]}"/>
<div attrs="{'invisible': [('rrule_type','!=','monthly')]}">
<field name="select1"/>
<field name="day"
attrs="{'required': [('select1','=','date'), ('rrule_type','=','monthly')],
'invisible': [('select1','=','day')]}"/>
<field name="byday" string="The"
attrs="{'required': [('select1','=','day'), ('rrule_type','=','monthly')], 'invisible': [('select1','=','date')]}"/>
<field name="week_list" nolabel="1"
attrs="{'required': [('select1','=','day'), ('rrule_type','=','monthly')], 'invisible': [('select1','=','date')]}"/>
</div>
<field name="date_deadline" groups="base.group_no_one"
attrs="{'invisible': ['|', ('allday','=',True), ('duration','&lt;', 24)]}"
on_change="onchange_dates(date,False,date_deadline)"/>
</group>
<group>
<field name="categ_ids" widget="many2many_tags"/>
<field name="location"/>
</group>
</group>
<label for="description"/>
<field name="description"/>
</page>
<page string="Options">
<group>
<field name="alarm_id" widget="selection" groups="base.group_no_one"/>
<field name="class"/>
<field name="show_as"/>
<field name="rrule" invisible="1" readonly="1"/>
<field name="recurrent_id_date" invisible="1"/>
<field name="recurrent_id" invisible="1"/>
<group col="1">
<group>
<field name="recurrency"/>
</group>
<group attrs="{'invisible': [('recurrency','=',False)]}">
<label for="interval"/>
<div>
<field name="interval" attrs="{'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="rrule_type" attrs="{'required': [('recurrency','==',True)]}" class="oe_inline"/>
</div>
<label string="Until" for="end_type"/>
<div>
<field name="end_type" attrs="{'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="count" attrs="{'invisible': [('end_type', '!=', 'count')], 'required': [('recurrency','==',True)]}" class="oe_inline"/>
<field name="end_date" attrs="{'invisible': [('end_type', '!=', 'end_date')], 'required': [('end_type', '=', 'end_date')]}" class="oe_inline"/>
</div>
<label string="Select Weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}"/>
<group col="2" colspan="1" name="weekdays" attrs="{'invisible' :[('rrule_type','not in', ['weekly'])]}">
<field name="mo"/>
<field name="tu"/>
<field name="we"/>
<field name="th"/>
<field name="fr"/>
<field name="sa"/>
<field name="su"/>
</group>
<label string="Day of Month"
attrs="{'invisible': [('rrule_type','!=','monthly')]}"/>
<div attrs="{'invisible': [('rrule_type','!=','monthly')]}">
<field name="select1"/>
<field name="day"
attrs="{'required': [('select1','=','date'), ('rrule_type','=','monthly')],
'invisible': [('select1','=','day')]}"/>
<field name="byday" string="The"
attrs="{'required': [('select1','=','day'), ('rrule_type','=','monthly')], 'invisible': [('select1','=','date')]}"/>
<field name="week_list" nolabel="1"
attrs="{'required': [('select1','=','day'), ('rrule_type','=','monthly')], 'invisible': [('select1','=','date')]}"/>
</div>
</group>
</group>
<group>
<field name="class"/>
<field name="show_as"/>
<field name="rrule" invisible="1" readonly="0" />
<field name="recurrent_id_date" invisible="1" />
<field name="recurrent_id" invisible="1" />
</group>
</group>
</group>
</page>
<page string="Invitations" groups="base.group_no_one">
<field name="attendee_ids" widget="one2many" mode="tree">
<tree string="Invitation details" editable="top" >
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="email" string="Mail To"/>
<field name="state"/>
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain" type="object"
icon="terp-crm" />
<button name="do_accept" string="Accept"
states="needs-action,tentative,declined"
type="object" icon="gtk-apply"/>
<button name="do_decline" string="Decline"
states="needs-action,tentative,accepted"
type="object" icon="gtk-cancel"/>
</tree>
<form string="Invitation details" version="7.0">
<header>
<button name="do_tentative" type="object"
</page>
<page string="Reminders" groups="base.group_no_one">
<field name="alarm_ids" />
</page>
<page string="Invitations" groups="base.group_no_one">
<field name="attendee_ids" widget="one2many" >
<tree string="Invitation details" editable="top" >
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="email" string="Mail To"/>
<field name="state"/>
<field name="email"/>
<field name="rsvp"/>
<field name="cutype"/>
<button name="do_tentative"
states="needs-action,declined,accepted"
string="Uncertain"/>
<button name="do_accept" type="object"
string="Uncertain" type="object"
icon="terp-crm" />
<button name="do_accept" string="Accept"
states="needs-action,tentative,declined"
string="Accept"/>
<button name="do_decline" type="object"
type="object" icon="gtk-apply"/>
<button name="do_decline" string="Decline"
states="needs-action,tentative,accepted"
string="Decline"/>
<field name="state" widget="statusbar" statusbar_visible="draft,open,done"/>
</header>
<group>
<group>
<field name="email"/>
<field name="rsvp"/>
<field name="cutype"/>
<field name="role"/>
</group>
<group>
<field name="partner_id"/>
<field name="user_id"/>
</group>
</group>
</form>
</field>
</page>
type="object" icon="gtk-cancel"/>
</tree>
</field>
</page>
<page string="DEBUG" groups="base.group_no_one">
<field name="rrule" invisible="0" readonly="0" />
<field name="recurrent_id_date" invisible="0" />
<field name="recurrent_id" invisible="0" />
</page>
</notebook>
</sheet>
<div class="oe_chatter">

View File

@ -1,8 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_calendar_attendee,calendar.attendee,model_calendar_attendee,,1,1,1,1
access_calendar_alarm,calendar.alarm,model_calendar_alarm,base.group_user,1,1,1,1
access_res_alarm,res.alarm,model_res_alarm,base.group_user,1,1,1,1
access_calendar_todo,calendar.todo,model_calendar_todo,base.group_user,1,1,1,1
access_calendar_event,calendar.event,model_calendar_event,base.group_user,1,1,1,1
access_calendar_attendee_survey_user,calendar.attendee,model_calendar_attendee,base.group_survey_user,1,0,0,0
access_crm_meeting_manager,crm.meeting.manager,model_crm_meeting,base.group_sale_manager,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_calendar_attendee calendar.attendee model_calendar_attendee 1 1 1 1
3 access_calendar_alarm calendar.alarm model_calendar_alarm base.group_user 1 1 1 1
access_res_alarm res.alarm model_res_alarm base.group_user 1 1 1 1
access_calendar_todo calendar.todo model_calendar_todo base.group_user 1 1 1 1
4 access_calendar_event calendar.event model_calendar_event base.group_user 1 1 1 1
5 access_calendar_attendee_survey_user calendar.attendee model_calendar_attendee base.group_survey_user 1 0 0 0
6 access_crm_meeting_manager crm.meeting.manager model_crm_meeting base.group_sale_manager 1 1 1 1

View File

@ -1,7 +1,7 @@
openerp.base_calendar = function(instance) {
var _t = instance.web._t;
var QWeb = instance.web.qweb;
instance.base_calendar = {}
var _t = instance.web._t;
var QWeb = instance.web.qweb;
instance.base_calendar = {}
instance.base_calendar.invitation = instance.web.Widget.extend({
@ -77,4 +77,4 @@ instance.base_calendar = {}
});
}
};
//vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,7 +1,7 @@
-
In Order to test base_calendar, I will first create One Simple Event with real data
-
!record {model: calendar.event, id: calendar_event_technicalpresentation0}:
!record {model: crm.meeting, id: crm_meeting_technicalpresentation0}:
class: private
date: '2011-04-30 16:00:00'
date_deadline: '2011-04-30 18:30:00'
@ -13,24 +13,24 @@
-
Now I will set recurrence for this event to occur monday and friday of week
-
!python {model: calendar.event}: |
!python {model: crm.meeting}: |
data = {'fr': 1, 'mo': 1, 'interval': 1, 'rrule_type': 'weekly', 'end_type': 'end_date', 'end_date': '2011-05-31 00:00:00', 'recurrency' : True}
self.write(cr, uid, [ref("calendar_event_technicalpresentation0")], data)
self.write(cr, uid, [ref("crm_meeting_technicalpresentation0")], data)
-
In order to check that recurrent events are views successfully in calendar view, I will open calendar view of events
-
!python {model: calendar.event}: |
!python {model: crm.meeting}: |
self.fields_view_get(cr, uid, False, 'calendar', context)
-
In order to check that recurrent events are views successfully in calendar view, I will search for one of the recurrent event and count the number of events
-
!python {model: calendar.event}: |
!python {model: crm.meeting}: |
ids = self.search(cr, uid, [('date', '>=', '2011-04-30 16:00:00'), ('date', '<=', '2011-05-31 00:00:00')], context={'virtual_id': True} )
assert len(ids) == 9, 'Wrong number of events found'
-
Now I will make All day event and test it
-
!record {model: calendar.event, id: calendar_event_alldaytestevent0}:
!record {model: crm.meeting, id: crm_meeting_alldaytestevent0}:
allday: 1
class: confidential
date: '2011-04-30 00:00:00'
@ -50,5 +50,5 @@
-
Now I will assign this reminder to all day event
-
!python {model: calendar.event}: |
self.write(cr, uid, [ref("calendar_event_alldaytestevent0")], {'alarm_id': ref("res_alarm_daybeforeeventstarts0")})
!python {model: crm.meeting}: |
self.write(cr, uid, [ref("crm_meeting_alldaytestevent0")], {'alarm_id': ref("res_alarm_daybeforeeventstarts0")})