[IMP] slaughtering + Merge calendar_event to crm_meeting + debug + ...
bzr revid: jke@openerp.com-20131108160549-2q6snfpxvbq18l0h
This commit is contained in:
parent
b92220ea7d
commit
d1c3269349
|
@ -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
|
@ -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>
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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','<', 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','<', 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">
|
||||
|
|
|
@ -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,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:
|
||||
|
||||
|
|
|
@ -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")})
|
||||
|
|
Loading…
Reference in New Issue