[IMP] calendar,google_calendar,google_calendar: allow modifying all event in recurrence

Before this commit, it was not possible to reapply all events in recurrence, delete all events of a recurrence or archive them.
This commit remove that limitation by reapplying the recurrence and remove all the existing events. When the events are synched, the action_mass_archive is used and it must ensure that only a request for the recurrency should be sent.
Individual request for each event are not sent.

Taskid: 2484335
Part-of: odoo/odoo#68700
This commit is contained in:
Arnaud Joset
2021-08-26 10:21:00 +00:00
parent 3466ccfe01
commit bb9f21d945
6 changed files with 359 additions and 92 deletions
+124 -15
View File
@@ -473,16 +473,18 @@ class Meeting(models.Model):
break_recurrence = values.get('recurrency') is False
update_alarms = False
update_time = False
if 'partner_ids' in values:
values['attendee_ids'] = self._attendees_values(values['partner_ids'])
update_alarms = True
# master arj todo: factorize use of _get_time_fields()
if any([values.get(key) for key in self.env['calendar.event']._get_time_fields()]) or 'alarm_ids' in values:
time_fields = self.env['calendar.event']._get_time_fields()
if any([values.get(key) for key in time_fields]) or 'alarm_ids' in values:
update_alarms = True
update_time = True
if (not recurrence_update_setting or recurrence_update_setting == 'self_only' and len(self) == 1) and 'follow_recurrence' not in values:
if any({field: values.get(field) for field in self.env['calendar.event']._get_time_fields() if field in values}):
if any({field: values.get(field) for field in time_fields if field in values}):
values['follow_recurrence'] = False
previous_attendees = self.attendee_ids
@@ -490,25 +492,36 @@ class Meeting(models.Model):
recurrence_values = {field: values.pop(field) for field in self._get_recurrent_fields() if field in values}
if update_recurrence:
if break_recurrence:
# Update this event
detached_events |= self._break_recurrence(future=recurrence_update_setting == 'future_events')
else:
update_start = self.start if recurrence_update_setting == 'future_events' else None
time_values = {field: values.pop(field) for field in self.env['calendar.event']._get_time_fields() if field in values}
if not update_start and (time_values or recurrence_values):
raise UserError(_("Updating All Events is not allowed when dates or time is modified. You can only update one particular event and following events."))
detached_events |= self._split_recurrence(time_values)
self.recurrence_id._write_events(values, dtstart=update_start)
future_update_start = self.start if recurrence_update_setting == 'future_events' else None
time_values = {field: values.pop(field) for field in time_fields if field in values}
if recurrence_update_setting == 'all_events':
# Update all events: we create a new reccurrence and dismiss the existing events
self._rewrite_recurrence(values, time_values, recurrence_values)
else:
# Update future events
detached_events |= self._split_recurrence(time_values)
self.recurrence_id._write_events(values, dtstart=future_update_start)
else:
super().write(values)
self._sync_activities(fields=values.keys())
if recurrence_update_setting != 'self_only' and not break_recurrence:
# We reapply recurrence for future events and when we add a rrule and 'recurrency' == True on the event
if recurrence_update_setting not in ['self_only', 'all_events'] and not break_recurrence:
detached_events |= self._apply_recurrence_values(recurrence_values, future=recurrence_update_setting == 'future_events')
(detached_events & self).active = False
(detached_events - self).with_context(archive_on_error=True).unlink()
if not self.env.context.get('dont_notify') and update_alarms:
self._setup_alarms()
attendee_update_events = self.filtered(lambda ev: ev.user_id != self.env.user)
if update_time and attendee_update_events:
# Another user update the event time fields. It should not be auto accepted for the organizer.
# This prevent weird behavior when a user modified future events time fields and
# the base event of a recurrence is accepted by the organizer but not the following events
attendee_update_events.attendee_ids.filtered(lambda att: self.user_id.partner_id == att.partner_id).write({'state': 'needsAction'})
current_attendees = self.filtered('active').attendee_ids
if 'partner_ids' in values:
@@ -635,6 +648,27 @@ class Meeting(models.Model):
if partner not in self.partner_ids:
self.write({'partner_ids': [(4, partner.id)]})
def action_mass_deletion(self, recurrence_update_setting):
self.ensure_one()
if recurrence_update_setting == 'all_events':
events = self.recurrence_id.calendar_event_ids
self.recurrence_id.unlink()
events.unlink()
elif recurrence_update_setting == 'future_events':
future_events = self.recurrence_id.calendar_event_ids.filtered(lambda ev: ev.start >= self.start)
future_events.unlink()
def action_mass_archive(self, recurrence_update_setting):
"""
The aim of this action purpose is to be called from sync calendar module when mass deletion is not possible.
"""
self.ensure_one()
if recurrence_update_setting == 'all_events':
self.recurrence_id.calendar_event_ids.write({'active': False})
elif recurrence_update_setting == 'future_events':
detached_events = self.recurrence_id._stop_at(self)
detached_events.write({'active': False})
# ------------------------------------------------------------
# MAILING
# ------------------------------------------------------------
@@ -734,11 +768,19 @@ class Meeting(models.Model):
:return: detached events
"""
self.ensure_one()
if not time_values:
return self.browse()
previous_week_day_field = weekday_to_field(self._get_start_date().weekday())
self.write(time_values)
before = previous_week_day_field = weekday_to_field(self._get_start_date().weekday())
if self.follow_recurrence and self.recurrency:
# arj fixme understand what is done here !!!! it can explain the issues...
previous_week_day_field = weekday_to_field(self._get_start_date().weekday())
else:
# arj fixme understand what is done here !!!! it can explain the issues...
# When we try to change recurrence values of an event not following the recurrence, we get the parameters from
# the base_event
previous_week_day_field = weekday_to_field(self.recurrence_id.base_event_id._get_start_date().weekday())
self.write({**time_values})
return self._apply_recurrence_values({
previous_week_day_field: False,
**self._get_recurrence_params(),
@@ -763,12 +805,79 @@ class Meeting(models.Model):
recurrences_to_unlink.with_context(archive_on_error=True).unlink()
return detached_events - self
def _rewrite_recurrence(self, values, time_values, recurrence_values):
""" Recreate the whole recurrence when all recurrent events must be moved
time_values corresponds to date times for one specific event. We need to update the base_event of the recurrence
and reapply the recurrence later. All exceptions are lost.
"""
self.ensure_one()
base_event = self.recurrence_id.base_event_id
if not base_event:
raise UserError(_("You can't update a recurrence without base event."))
[base_time_values] = self.recurrence_id.base_event_id.read(['start', 'stop', 'allday'])
update_dict = {}
start_update = fields.Datetime.to_datetime(time_values.get('start'))
stop_update = fields.Datetime.to_datetime(time_values.get('stop'))
# Convert the base_event_id hours according to new values: time shift
if start_update or stop_update:
if start_update:
start = base_time_values['start'] + (start_update - self.start)
stop = base_time_values['stop'] + (start_update - self.start)
start_date = base_time_values['start'].date() + (start_update.date() - self.start.date())
stop_date = base_time_values['stop'].date() + (start_update.date() - self.start.date())
update_dict.update({'start': start, 'start_date': start_date, 'stop': stop, 'stop_date': stop_date})
if stop_update:
if not start_update:
# Apply the same shift for start
start = base_time_values['start'] + (stop_update - self.stop)
start_date = base_time_values['start_date'] + (stop_update.date() - self.stop.date())
update_dict.update({'start': start, 'start_date': start_date})
stop = base_time_values['stop'] + (stop_update - self.stop)
stop_date = base_time_values['stop'].date() + (stop_update.date() - self.stop.date())
update_dict.update({'stop': stop, 'stop_date': stop_date})
time_values.update(update_dict)
if time_values or recurrence_values:
rec_fields = list(self._get_recurrent_fields())
[rec_vals] = base_event.read(rec_fields)
old_recurrence_values = {field: rec_vals.pop(field) for field in rec_fields if
field in rec_vals}
base_event.write({**values, **time_values})
# Delete all events except the base event and the currently modified
expandable_events = self.recurrence_id.calendar_event_ids - (self.recurrence_id.base_event_id + self)
self.recurrence_id.with_context(archive_on_error=True).unlink()
expandable_events.with_context(archive_on_error=True).unlink()
# Make sure to recreate a new recurrence. Needed to prevent sync issues
base_event.recurrence_id = False
# Recreate all events and the recurrence: override updated values
new_values = {
**old_recurrence_values,
**base_event._get_recurrence_params(),
**recurrence_values,
}
new_values.pop('rrule')
detached_events = base_event._apply_recurrence_values(new_values)
detached_events.write({'active': False})
# archive the current event if all the events were recreated
if self != self.recurrence_id.base_event_id and time_values:
self.active = False
else:
# Write on all events. Carefull, it could trigger a lot of noise to Google/Microsoft...
self.recurrence_id._write_events(values)
# ------------------------------------------------------------
# MANAGEMENT
# ------------------------------------------------------------
def change_attendee_status(self, status):
attendee = self.attendee_ids.filtered(lambda x: x.partner_id == self.env.user.partner_id)
def change_attendee_status(self, status, recurrence_update_setting):
self.ensure_one()
if recurrence_update_setting == 'all_events':
events = self.recurrence_id.calendar_event_ids
elif recurrence_update_setting == 'future_events':
events = self.recurrence_id.calendar_event_ids.filtered(lambda ev: ev.start >= self.start)
else:
events = self
attendee = events.attendee_ids.filtered(lambda x: x.partner_id == self.env.user.partner_id)
if status == 'accepted':
return attendee.do_accept()
if status == 'declined':
@@ -17,6 +17,12 @@
<input name="recurrence-update" type="radio" class="form-check-input o_radio_input" value="future_events" id="future_events"/>
<label class="form-check-label o_form_label" for="future_events">This and following events</label>
</div>
<div class="form-check o_radio_item">
<input name="recurrence-update" type="radio" class="form-check-input o_radio_input" value="all_events" id="all_events"/>
<label class="form-check-label o_form_label" for="all_events">All events</label>
</div>
</div>
<t t-extend="mail.systray.ActivityMenu.Previews">
+122 -58
View File
@@ -364,12 +364,25 @@ class TestUpdateRecurrentEvents(TestRecurrentEvents):
def test_shift_all(self):
event = self.events[1]
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(days=4),
'stop': event.stop + relativedelta(days=5),
})
self.assertEventDates(event.recurrence_id.calendar_event_ids, [
(datetime(2019, 10, 22, 1, 0), datetime(2019, 10, 24, 18, 0)),
(datetime(2019, 10, 29, 1, 0), datetime(2019, 10, 31, 18, 0)),
(datetime(2019, 11, 5, 1, 0), datetime(2019, 11, 7, 18, 0)),
])
event.write({
'recurrence_update': 'all_events',
'tue': False,
'fri': False,
'sat': True,
'start': event.start + relativedelta(days=4),
'stop': event.stop + relativedelta(days=5),
})
recurrence = self.env['calendar.recurrence'].search([])
self.assertEventDates(recurrence.calendar_event_ids, [
(datetime(2019, 10, 26, 1, 0), datetime(2019, 10, 29, 18, 0)),
(datetime(2019, 11, 2, 1, 0), datetime(2019, 11, 5, 18, 0)),
(datetime(2019, 11, 9, 1, 0), datetime(2019, 11, 12, 18, 0)),
])
def test_change_week_day_rrule(self):
recurrence = self.events.recurrence_id
@@ -380,27 +393,35 @@ class TestUpdateRecurrentEvents(TestRecurrentEvents):
def test_shift_all_base_inactive(self):
self.recurrence.base_event_id.active = False
event = self.events[1]
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(days=4),
'stop': event.stop + relativedelta(days=5),
})
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(days=4),
'stop': event.stop + relativedelta(days=5),
})
self.assertFalse(self.recurrence.calendar_event_ids, "Inactive event should not create recurrent events")
def test_shift_all_with_outlier(self):
outlier = self.events[1]
outlier.write({
'recurrence_update': 'self_only',
'start': datetime(2019, 9, 26, 1, 0), # Thursday
'stop': datetime(2019, 9, 26, 18, 0),
'start': datetime(2019, 10, 31, 1, 0), # Thursday
'stop': datetime(2019, 10, 31, 18, 0),
})
event = self.events[0]
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(days=4),
'stop': event.stop + relativedelta(days=5),
})
event.write({
'recurrence_update': 'all_events',
'tue': False,
'fri': False,
'sat': True,
'start': event.start + relativedelta(days=4),
'stop': event.stop + relativedelta(days=4),
})
self.assertEventDates(event.recurrence_id.calendar_event_ids, [
(datetime(2019, 10, 26, 1, 0), datetime(2019, 10, 28, 18, 0)),
(datetime(2019, 11, 2, 1, 0), datetime(2019, 11, 4, 18, 0)),
(datetime(2019, 11, 9, 1, 0), datetime(2019, 11, 11, 18, 0))
])
self.assertFalse(outlier.exists(), 'The outlier should have been deleted')
def test_update_recurrence_future(self):
event = self.events[1]
@@ -429,11 +450,16 @@ class TestUpdateRecurrentEvents(TestRecurrentEvents):
self.assertTrue(event.recurrence_id.fri)
def test_update_recurrence_all(self):
with self.assertRaises(UserError):
self.events[1].write({
'recurrence_update': 'all_events',
'mon': True, # recurrence is now Tuesday AND Monday
})
self.events[1].write({
'recurrence_update': 'all_events',
'mon': True, # recurrence is now Tuesday AND Monday
})
recurrence = self.env['calendar.recurrence'].search([])
# Monday and Tuesday but not in the past
self.assertEventDates(recurrence.calendar_event_ids, [
(datetime(2019, 10, 22, 1, 0), datetime(2019, 10, 24, 18, 0)),
(datetime(2019, 10, 28, 1, 0), datetime(2019, 10, 30, 18, 0)),
])
def test_shift_single(self):
event = self.events[1]
@@ -513,7 +539,26 @@ class TestUpdateRecurrentEvents(TestRecurrentEvents):
(datetime(2019, 11, 9, 8, 0), datetime(2019, 11, 12, 18, 0)),
])
# TODO test followers, and alarms are copied
def test_archive_recurrence_all(self):
self.events[1].action_mass_archive('all_events')
self.assertEqual([False, False, False], self.events.mapped('active'))
def test_archive_recurrence_future(self):
event = self.events[1]
event.action_mass_archive('future_events')
self.assertEqual([True, False, False], self.events.mapped('active'))
def test_unlink_recurrence_all(self):
event = self.events[1]
event.action_mass_deletion('all_events')
self.assertFalse(self.recurrence.exists())
self.assertFalse(self.events.exists())
def test_unlink_recurrence_future(self):
event = self.events[1]
event.action_mass_deletion('future_events')
self.assertTrue(self.recurrence)
self.assertEqual(self.events.exists(), self.events[0])
class TestUpdateMultiDayWeeklyRecurrentEvents(TestRecurrentEvents):
@@ -541,21 +586,39 @@ class TestUpdateMultiDayWeeklyRecurrentEvents(TestRecurrentEvents):
def test_shift_all_multiple_weekdays(self):
event = self.events[0] # Tuesday
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(days=2),
'stop': event.stop + relativedelta(days=2),
})
# We go from 2 days a week Thuesday and Friday to one day a week, Thursday
event.write({
'recurrence_update': 'all_events',
'tue': False,
'thu': True,
'fri': False,
'start': event.start + relativedelta(days=2),
'stop': event.stop + relativedelta(days=2),
})
recurrence = self.env['calendar.recurrence'].search([])
# We don't try to do magic tricks. First event is moved, other remain
self.assertEventDates(recurrence.calendar_event_ids, [
(datetime(2019, 10, 24, 1, 0), datetime(2019, 10, 26, 18, 0)),
(datetime(2019, 10, 31, 1, 0), datetime(2019, 11, 2, 18, 0)),
(datetime(2019, 11, 7, 1, 0), datetime(2019, 11, 9, 18, 0)),
])
def test_shift_all_multiple_weekdays_duration(self):
event = self.events[0] # Tuesday
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(days=2),
'stop': event.stop + relativedelta(days=3),
})
event.write({
'recurrence_update': 'all_events',
'tue': False,
'thu': True,
'fri': False,
'start': event.start + relativedelta(days=2),
'stop': event.stop + relativedelta(days=3),
})
recurrence = self.env['calendar.recurrence'].search([])
self.assertEventDates(recurrence.calendar_event_ids, [
(datetime(2019, 10, 24, 1, 0), datetime(2019, 10, 27, 18, 0)),
(datetime(2019, 10, 31, 1, 0), datetime(2019, 11, 3, 18, 0)),
(datetime(2019, 11, 7, 1, 0), datetime(2019, 11, 10, 18, 0)),
])
def test_shift_future_multiple_weekdays(self):
event = self.events[1] # Friday
@@ -598,12 +661,17 @@ class TestUpdateMonthlyByDay(TestRecurrentEvents):
def test_shift_all(self):
event = self.events[1]
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'start': event.start - relativedelta(days=5),
'stop': event.stop - relativedelta(days=4),
})
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(hours=5),
'stop': event.stop + relativedelta(hours=5),
})
recurrence = self.env['calendar.recurrence'].search([])
self.assertEventDates(recurrence.calendar_event_ids, [
(datetime(2019, 10, 15, 6, 0), datetime(2019, 10, 16, 23, 0)),
(datetime(2019, 11, 19, 6, 0), datetime(2019, 11, 20, 23, 0)),
(datetime(2019, 12, 17, 6, 0), datetime(2019, 12, 18, 23, 0)),
])
class TestUpdateMonthlyByDate(TestRecurrentEvents):
@@ -644,19 +712,15 @@ class TestUpdateMonthlyByDate(TestRecurrentEvents):
(datetime(2019, 12, 26, 1, 0), datetime(2019, 12, 29, 18, 0)),
])
def test_shift_all(self):
event = self.events[1]
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'start': event.start + relativedelta(days=4),
'stop': event.stop + relativedelta(days=5),
})
def test_update_all(self):
event = self.events[1]
with self.assertRaises(UserError):
event.write({
'recurrence_update': 'all_events',
'day': 25,
})
event.write({
'recurrence_update': 'all_events',
'day': 25,
})
recurrence = self.env['calendar.recurrence'].search([('day', '=', 25)])
self.assertEventDates(recurrence.calendar_event_ids, [
(datetime(2019, 10, 25, 1, 0), datetime(2019, 10, 27, 18, 0)),
(datetime(2019, 11, 25, 1, 0), datetime(2019, 11, 27, 18, 0)),
(datetime(2019, 12, 25, 1, 0), datetime(2019, 12, 27, 18, 0)),
])
@@ -172,15 +172,35 @@ const GoogleCalendarController = CalendarController.include({
});
},
_onArchiveRecord: function (event) {
var self = this;
Dialog.confirm(this, _t("Are you sure you want to archive this record ?"), {
confirm_callback: function () {
self.model.archiveRecords([event.data.id], self.modelName).then(function () {
_onArchiveRecord: async function (event) {
const self = this;
if (event.data.event.record.recurrency) {
const recurrenceUpdate = await this._askRecurrenceUpdatePolicy();
event.data = Object.assign({}, event.data, {
'recurrenceUpdate': recurrenceUpdate,
});
if (recurrenceUpdate === 'self_only') {
self.model.archiveRecords([event.data.id], self.modelName).then(function () {
self.reload();
});
}
});
} else {
return this._rpc({
model: self.modelName,
method: 'action_mass_archive',
args: [[event.data.id], recurrenceUpdate],
}).then( function () {
self.reload();
});
}
} else {
Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), {
confirm_callback: function () {
self.model.archiveRecords([event.data.id], self.modelName).then(function () {
self.reload();
});
}
});
}
},
});
@@ -291,7 +311,7 @@ const GoogleCalendarRenderer = CalendarRenderer.include({
_onArchiveEvent: function (event) {
this._unselectEvent();
this.trigger_up('archiveRecord', {id: parseInt(event.data.id, 10)});
this.trigger_up('archiveRecord', {id: parseInt(event.data.id, 10), event: event.target.event.extendedProps});
},
});
@@ -161,15 +161,35 @@ const MicrosoftCalendarController = CalendarController.include({
});
},
_onArchiveRecord: function (event) {
var self = this;
Dialog.confirm(this, _t("Are you sure you want to archive this record ?"), {
confirm_callback: function () {
self.model.archiveRecords([event.data.id], self.modelName).then(function () {
_onArchiveRecord: async function (event) {
const self = this;
if (event.data.event.record.recurrency) {
const recurrenceUpdate = await this._askRecurrenceUpdatePolicy();
event.data = Object.assign({}, event.data, {
'recurrenceUpdate': recurrenceUpdate,
});
if (recurrenceUpdate === 'self_only') {
self.model.archiveRecords([event.data.id], self.modelName).then(function () {
self.reload();
});
}
});
} else {
return this._rpc({
model: self.modelName,
method: 'action_mass_archive',
args: [[event.data.id], recurrenceUpdate],
}).then( function () {
self.reload();
});
}
} else {
Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), {
confirm_callback: function () {
self.model.archiveRecords([event.data.id], self.modelName).then(function () {
self.reload();
});
}
});
}
},
});
@@ -280,7 +300,7 @@ const MicrosoftCalendarRenderer = CalendarRenderer.include({
_onArchiveEvent: function (event) {
this._unselectEvent();
this.trigger_up('archiveRecord', {id: parseInt(event.data.id, 10)});
this.trigger_up('archiveRecord', {id: parseInt(event.data.id, 10), event: event.target.event.extendedProps});
},
});
@@ -39,6 +39,7 @@ var CalendarController = AbstractController.extend({
quickCreate: '_onQuickCreate',
updateRecord: '_onUpdateRecord',
viewUpdated: '_onViewUpdated',
AttendeeStatus: '_onAttendeeStatus',
}),
events: _.extend({}, AbstractController.prototype.events, {
'click button.o_calendar_button_new': '_onButtonNew',
@@ -271,15 +272,37 @@ var CalendarController = AbstractController.extend({
* @private
* @param {OdooEvent} event
*/
_onDeleteRecord: function (event) {
_onDeleteRecord: async function (event) {
var self = this;
Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), {
if (event.data.event.record.recurrency) {
const recurrenceUpdate = await this._askRecurrenceUpdatePolicy();
event.data = _.extend({}, event.data, {
'recurrenceUpdate': recurrenceUpdate,
});
if (recurrenceUpdate === 'self_only') {
self.model.deleteRecords([event.data.id], self.modelName).then(function () {
self.reload();
});
} else {
return this._rpc({
model: self.modelName,
method: 'action_mass_deletion',
args: [[event.data.id], recurrenceUpdate],
}).then( function () {
self.reload();
});
}
} else {
Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), {
confirm_callback: function () {
self.model.deleteRecords([event.data.id], self.modelName).then(function () {
self.reload();
});
}
});
}
},
/**
* @private
@@ -351,7 +374,7 @@ var CalendarController = AbstractController.extend({
return;
}
const title = _t('New Event')
const title = _t('New Event');
if (this.eventOpenPopup) {
if (this.previousOpen) { this.previousOpen.close(); }
this.previousOpen = new dialogs.FormViewDialog(self, {
@@ -486,6 +509,31 @@ var CalendarController = AbstractController.extend({
const title = `${this.displayName} (${event.data.title})`;
return this.updateControlPanel({ title });
},
/**
* Update Attendee status in batch for recurrent events
* @private
* @param {OdooEvent} event
*/
_onAttendeeStatus: async function(event) {
const self = this;
let recurrenceUpdate;
if (event.data.record.recurrency) {
recurrenceUpdate = await this._askRecurrenceUpdatePolicy();
event.data = _.extend({}, event.data, {
'recurrenceUpdate': recurrenceUpdate,
});
} else {
recurrenceUpdate = false;
}
return this._rpc({
model: self.modelName,
method: 'change_attendee_status',
args: [[event.data.id], event.data.selectedStatus, recurrenceUpdate],
}).then( function () {
self.reload();
});
}
});
return CalendarController;