diff --git a/addons/calendar/data/mail_template_data.xml b/addons/calendar/data/mail_template_data.xml index 9432f2d67f7..e9866a0be7a 100644 --- a/addons/calendar/data/mail_template_data.xml +++ b/addons/calendar/data/mail_template_data.xml @@ -74,7 +74,7 @@ -
  • When: Every 1 Weeks, for 3 events
  • +
  • When: Every 1 Weeks, for 3 events
  • Duration: 0H30
  • @@ -205,7 +205,7 @@
    -
  • When: Every 1 Weeks, for 3 events
  • +
  • When: Every 1 Weeks, for 3 events
  • Duration: 0H30
  • @@ -312,7 +312,7 @@
    -
  • When: Every 1 Weeks, for 3 events
  • +
  • When: Every 1 Weeks, for 3 events
  • Duration: 0H30
  • @@ -423,7 +423,7 @@
    -
  • When: Every 1 Weeks, for 3 events
  • +
  • When: Every 1 Weeks, for 3 events
  • Duration: diff --git a/addons/calendar/i18n/calendar.pot b/addons/calendar/i18n/calendar.pot index 6020d145c32..a21c011fe54 100644 --- a/addons/calendar/i18n/calendar.pot +++ b/addons/calendar/i18n/calendar.pot @@ -128,7 +128,7 @@ msgid "" "
  • \n" "
    \n" " \n" -"
  • When: Every 1 Weeks, for 3 events
  • \n" +"
  • When: Every 1 Weeks, for 3 events
  • \n" "
    \n" " \n" "
  • Duration: 0H30
  • \n" @@ -170,110 +170,6 @@ msgid "" " " msgstr "" -#. module: calendar -#: model:mail.template,body_html:calendar.calendar_template_meeting_invitation -msgid "" -"
    \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"\n" -"

    \n" -" Hello Wood Corner,

    \n" -"\n" -" \n" -" Colleen Diaz invited you for the Follow-up for Project proposal meeting.\n" -" \n" -" \n" -" Your meeting Follow-up for Project proposal has been booked.\n" -" \n" -"\n" -"

    \n" -"
    \n" -" \n" -" Accept\n" -" \n" -" Decline\n" -" View\n" -"
    \n" -" \n" -" \n" -" \n" -" \n" -"
    \n" -"
    \n" -" Tuesday\n" -"
    \n" -"
    \n" -" 4\n" -"
    \n" -"
    \n" -" May 2021\n" -"
    \n" -"
    \n" -" \n" -"
    \n" -" 11:00 AM\n" -"
    \n" -" \n" -"
    \n" -" (Europe/Brussels)\n" -"
    \n" -"
    \n" -"
    \n" -"
    \n" -"
    \n" -"

    Details of the event

    \n" -"
      \n" -" \n" -"
    • Location: Bruxelles\n" -" (View Map)\n" -"
    • \n" -"
      \n" -" \n" -"
    • When: Every 1 Weeks, for 3 events
    • \n" -"
      \n" -" \n" -"
    • Duration: 0H30
    • \n" -"
      \n" -"
    • Attendees\n" -"
        \n" -"
      • \n" -"
        \n" -" \n" -" Mitchell Admin\n" -" \n" -" \n" -" You\n" -" \n" -"
      • \n" -"
    • \n" -" \n" -"
    • \n" -" How to Join:\n" -" Join with Odoo Discuss\n" -" Join at
      \n" -" www.mycompany.com/calendar/join_videocall/xyz\n" -"
    • \n" -"
      \n" -" \n" -"
    • Description of the event:\n" -" Internal meeting for discussion for new pricing for product and services.
    • \n" -"
      \n" -"
    \n" -"
    \n" -"
    \n" -" Thank you,\n" -" \n" -"
    \n" -" --
    Mitchell Admin
    \n" -"
    \n" -"
    \n" -" " -msgstr "" - #. module: calendar #: model:mail.template,body_html:calendar.calendar_template_meeting_update msgid "" @@ -334,7 +230,7 @@ msgid "" " \n" "
    \n" " \n" -"
  • When: Every 1 Weeks, for 3 events
  • \n" +"
  • When: Every 1 Weeks, for 3 events
  • \n" "
    \n" " \n" "
  • Duration:\n" @@ -359,6 +255,141 @@ msgid "" " " msgstr "" +#. module: calendar +#: model:mail.template,body_html:calendar.calendar_template_meeting_invitation +msgid "" +"
    \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +"

    \n" +" Hello Wood Corner,

    \n" +"\n" +" \n" +" Your appointment Schedule a Demo with Ready Mat has been booked.\n" +" \n" +" You will receive a mail of confirmation with more details when your appointment will be confirmed.\n" +" \n" +" \n" +" \n" +" \n" +" scheduled the following appointment Schedule a Demo with you.\n" +" \n" +" \n" +" Your appointment Schedule a Demo has been booked.\n" +" \n" +" \n" +" \n" +" Colleen Diaz invited you for the Follow-up for Project proposal meeting.\n" +" \n" +" \n" +" Your meeting Follow-up for Project proposal has been booked.\n" +" \n" +"\n" +"

    \n" +"
    \n" +" \n" +" \n" +" Accept\n" +" \n" +" Decline\n" +" \n" +" View\n" +"
    \n" +" \n" +" \n" +" \n" +" \n" +"
    \n" +"
    \n" +" Tuesday\n" +"
    \n" +"
    \n" +" 4\n" +"
    \n" +"
    \n" +" May 2021\n" +"
    \n" +"
    \n" +" \n" +"
    \n" +" 11:00 AM\n" +"
    \n" +" \n" +"
    \n" +" (Europe/Brussels)\n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"
    \n" +"

    Details of the event

    \n" +"
      \n" +" \n" +"
    • Appointment Type: Schedule a Demo
    • \n" +"
      \n" +" \n" +"
    • Location: Bruxelles\n" +" (View Map)\n" +"
    • \n" +"
      \n" +" \n" +"
    • When: Every 1 Weeks, for 3 events
    • \n" +"
      \n" +" \n" +"
    • Duration: 0H30
    • \n" +"
      \n" +"
    • Attendees\n" +"
        \n" +"
      • \n" +"
        \n" +" \n" +" Mitchell Admin\n" +" \n" +" \n" +" You\n" +" \n" +"
      • \n" +"
    • \n" +"
    • \n" +" For: people\n" +"
    • \n" +"
    • \n" +" Resources\n" +"
        \n" +"
      • \n" +" Table 1\n" +"
      • \n" +"
      \n" +"
    • \n" +" \n" +"
    • \n" +" How to Join:\n" +" Join with Odoo Discuss\n" +" Join at
      \n" +" www.mycompany.com/calendar/join_videocall/xyz\n" +"
    • \n" +"
      \n" +" \n" +"
    • Description of the event:\n" +" Internal meeting for discussion for new pricing for product and services.
    • \n" +"
      \n" +"
    \n" +"
    \n" +"
    \n" +" Thank you,\n" +" \n" +"
    \n" +" --
    Mitchell Admin
    \n" +"
    \n" +"
    \n" +" " +msgstr "" + #. module: calendar #: model:mail.template,body_html:calendar.calendar_template_meeting_reminder msgid "" @@ -412,7 +443,7 @@ msgid "" "
  • \n" "
    \n" " \n" -"
  • When: Every 1 Weeks, for 3 events
  • \n" +"
  • When: Every 1 Weeks, for 3 events
  • \n" "
    \n" " \n" "
  • Duration: 0H30
  • \n" @@ -1196,7 +1227,108 @@ msgstr "" #. odoo-python #: code:addons/calendar/models/calendar_recurrence.py:0 #, python-format -msgid "Every %(count)s %(period)s" +msgid "Every %(interval)s Days" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Days for %(count)s events" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Days until %(until)s" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Months day %(day)s" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Months day %(day)s for %(count)s events" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Months day %(day)s until %(until)s" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Months on the %(position)s %(weekday)s" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "" +"Every %(interval)s Months on the %(position)s %(weekday)s for %(count)s " +"events" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "" +"Every %(interval)s Months on the %(position)s %(weekday)s until %(until)s" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Weeks on %(days)s" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Weeks on %(days)s for %(count)s events" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Weeks on %(days)s until %(until)s" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Years" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Years for %(count)s events" +msgstr "" + +#. module: calendar +#. odoo-python +#: code:addons/calendar/models/calendar_recurrence.py:0 +#, python-format +msgid "Every %(interval)s Years until %(until)s" msgstr "" #. module: calendar @@ -2400,13 +2532,6 @@ msgstr "" msgid "awaiting" msgstr "" -#. module: calendar -#. odoo-python -#: code:addons/calendar/models/calendar_recurrence.py:0 -#, python-format -msgid "day %s" -msgstr "" - #. module: calendar #. odoo-javascript #: code:addons/calendar/static/src/views/fields/many2many_attendee_expandable.xml:0 @@ -2442,24 +2567,8 @@ msgid "no," msgstr "" #. module: calendar -#. odoo-python -#: code:addons/calendar/models/calendar_recurrence.py:0 -#, python-format -msgid "on %s" -msgstr "" - -#. module: calendar -#. odoo-python -#: code:addons/calendar/models/calendar_recurrence.py:0 -#, python-format -msgid "on the %(position)s %(weekday)s" -msgstr "" - -#. module: calendar -#. odoo-javascript -#: code:addons/calendar/static/src/views/fields/many2many_attendee.xml:0 -#, python-format -msgid "props.placeholder" +#: model_terms:ir.ui.view,arch_db:calendar.view_calendar_event_form +msgid "to" msgstr "" #. module: calendar @@ -2469,13 +2578,6 @@ msgstr "" msgid "uncertain" msgstr "" -#. module: calendar -#. odoo-python -#: code:addons/calendar/models/calendar_recurrence.py:0 -#, python-format -msgid "until %s" -msgstr "" - #. module: calendar #: model_terms:ir.ui.view,arch_db:calendar.view_calendar_event_form msgid "yes," diff --git a/addons/calendar/models/calendar_recurrence.py b/addons/calendar/models/calendar_recurrence.py index fad03b3a101..2cfd25ec4e8 100644 --- a/addons/calendar/models/calendar_recurrence.py +++ b/addons/calendar/models/calendar_recurrence.py @@ -131,38 +131,70 @@ class RecurrenceRule(models.Model): "The day must be between 1 and 31"), ] + def _get_daily_recurrence_name(self): + if self.end_type == 'count': + return _("Every %(interval)s Days for %(count)s events", interval=self.interval, count=self.count) + if self.end_type == 'end_date': + return _("Every %(interval)s Days until %(until)s", interval=self.interval, until=self.until) + return _("Every %(interval)s Days", interval=self.interval) + + def _get_weekly_recurrence_name(self): + weekday_selection = dict(self._fields['weekday']._description_selection(self.env)) + weekdays = self._get_week_days() + # Convert Weekday object + weekdays = [str(w) for w in weekdays] + # We need to get the day full name from its three first letters. + week_map = {v: k for k, v in RRULE_WEEKDAYS.items()} + weekday_short = [week_map[w] for w in weekdays] + day_strings = [weekday_selection[day] for day in weekday_short] + days = ", ".join(day_strings) + + if self.end_type == 'count': + return _("Every %(interval)s Weeks on %(days)s for %(count)s events", interval=self.interval, days=days, count=self.count) + if self.end_type == 'end_date': + return _("Every %(interval)s Weeks on %(days)s until %(until)s", interval=self.interval, days=days, until=self.until) + return _("Every %(interval)s Weeks on %(days)s", interval=self.interval, days=days) + + def _get_monthly_recurrence_name(self): + if self.month_by == 'day': + weekday_selection = dict(self._fields['weekday']._description_selection(self.env)) + byday_selection = dict(self._fields['byday']._description_selection(self.env)) + position_label = byday_selection[self.byday] + weekday_label = weekday_selection[self.weekday] + + if self.end_type == 'count': + return _("Every %(interval)s Months on the %(position)s %(weekday)s for %(count)s events", interval=self.interval, position=position_label, weekday=weekday_label, count=self.count) + if self.end_type == 'end_date': + return _("Every %(interval)s Months on the %(position)s %(weekday)s until %(until)s", interval=self.interval, position=position_label, weekday=weekday_label, until=self.until) + return _("Every %(interval)s Months on the %(position)s %(weekday)s", interval=self.interval, position=position_label, weekday=weekday_label) + else: + if self.end_type == 'count': + return _("Every %(interval)s Months day %(day)s for %(count)s events", interval=self.interval, day=self.day, count=self.count) + if self.end_type == 'end_date': + return _("Every %(interval)s Months day %(day)s until %(until)s", interval=self.interval, day=self.day, until=self.until) + return _("Every %(interval)s Months day %(day)s", interval=self.interval, day=self.day) + + def _get_yearly_recurrence_name(self): + if self.end_type == 'count': + return _("Every %(interval)s Years for %(count)s events", interval=self.interval, count=self.count) + if self.end_type == 'end_date': + return _("Every %(interval)s Years until %(until)s", interval=self.interval, until=self.until) + return _("Every %(interval)s Years", interval=self.interval) + + def get_recurrence_name(self): + if self.rrule_type == 'daily': + return self._get_daily_recurrence_name() + if self.rrule_type == 'weekly': + return self._get_weekly_recurrence_name() + if self.rrule_type == 'monthly': + return self._get_monthly_recurrence_name() + if self.rrule_type == 'yearly': + return self._get_yearly_recurrence_name() + @api.depends('rrule') def _compute_name(self): for recurrence in self: - period = dict(RRULE_TYPE_SELECTION)[recurrence.rrule_type] - every = _("Every %(count)s %(period)s", count=recurrence.interval, period=period) - - if recurrence.end_type == 'count': - end = _("for %s events", recurrence.count) - elif recurrence.end_type == 'end_date': - end = _("until %s", recurrence.until) - else: - end = '' - - if recurrence.rrule_type == 'weekly': - weekdays = recurrence._get_week_days() - # Convert Weekday object - weekdays = [str(w) for w in weekdays] - # We need to get the day full name from its three first letters. - week_map = {v: k for k, v in RRULE_WEEKDAYS.items()} - weekday_short = [week_map[w] for w in weekdays] - day_strings = [d[1] for d in WEEKDAY_SELECTION if d[0] in weekday_short] - on = _("on %s", ", ".join(day_strings)) - elif recurrence.rrule_type == 'monthly': - if recurrence.month_by == 'day': - position_label = dict(BYDAY_SELECTION)[recurrence.byday] - weekday_label = dict(WEEKDAY_SELECTION)[recurrence.weekday] - on = _("on the %(position)s %(weekday)s", position=position_label, weekday=weekday_label) - else: - on = _("day %s", recurrence.day) - else: - on = '' - recurrence.name = ' '.join(filter(lambda s: s, [every, on, end])) + recurrence.name = recurrence.get_recurrence_name() @api.depends('calendar_event_ids.start') def _compute_dtstart(self): diff --git a/addons/calendar/tests/__init__.py b/addons/calendar/tests/__init__.py index 78a1c408fd4..1affae4fe1b 100644 --- a/addons/calendar/tests/__init__.py +++ b/addons/calendar/tests/__init__.py @@ -10,3 +10,4 @@ from . import test_event_recurrence from . import test_event_notifications from . import test_mail_activity_mixin from . import test_res_partner +from . import test_recurrence_rule diff --git a/addons/calendar/tests/test_recurrence_rule.py b/addons/calendar/tests/test_recurrence_rule.py new file mode 100644 index 00000000000..8843c3b66dc --- /dev/null +++ b/addons/calendar/tests/test_recurrence_rule.py @@ -0,0 +1,175 @@ +from datetime import datetime + +from odoo.tests.common import TransactionCase + + +class TestRecurrenceRule(TransactionCase): + + def test_daily_count(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'daily', + 'interval': 2, + 'count': 3, + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Days for 3 events') + + def test_daily_until(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'daily', + 'interval': 2, + 'end_type': 'end_date', + 'until': datetime(2024, 11, 15), + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Days until 2024-11-15') + + def test_daily_none(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'daily', + 'interval': 2, + 'end_type': '', + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Days') + + def test_weekly_count(self): + """ Every week, on Tuesdays, for 3 occurences """ + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'weekly', + 'tue': True, + 'wed': True, + 'interval': 2, + 'count': 3, + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Weeks on Tuesday, Wednesday for 3 events') + + def test_weekly_until(self): + """ Every week, on Tuesdays, for 3 occurences """ + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'weekly', + 'tue': True, + 'wed': True, + 'interval': 2, + 'end_type': 'end_date', + 'until': datetime(2024, 11, 15), + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Weeks on Tuesday, Wednesday until 2024-11-15') + + def test_weekly_none(self): + """ Every week, on Tuesdays, for 3 occurences """ + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'weekly', + 'tue': True, + 'wed': True, + 'interval': 2, + 'end_type': '', + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Weeks on Tuesday, Wednesday') + + def test_monthly_count_by_day(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'monthly', + 'interval': 2, + 'month_by': 'day', + 'byday': '1', + 'weekday': 'MON', + 'end_type': 'count', + 'count': 3, + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Months on the First Monday for 3 events') + + def test_monthly_until_by_day(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'monthly', + 'interval': 2, + 'month_by': 'day', + 'byday': '1', + 'weekday': 'MON', + 'end_type': 'end_date', + 'until': datetime(2024, 11, 15), + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Months on the First Monday until 2024-11-15') + + def test_monthly_none_by_day(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'monthly', + 'interval': 2, + 'month_by': 'day', + 'byday': '1', + 'weekday': 'MON', + 'end_type': '', + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Months on the First Monday') + + def test_monthly_count_by_date(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'monthly', + 'interval': 2, + 'month_by': 'date', + 'day': 27, + 'weekday': 'MON', + 'end_type': 'count', + 'count': 3, + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Months day 27 for 3 events') + + def test_monthly_until_by_date(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'monthly', + 'interval': 2, + 'month_by': 'date', + 'day': 27, + 'weekday': 'MON', + 'end_type': 'end_date', + 'until': datetime(2024, 11, 15), + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Months day 27 until 2024-11-15') + + def test_monthly_none_by_date(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'monthly', + 'interval': 2, + 'month_by': 'date', + 'day': 27, + 'weekday': 'MON', + 'end_type': '', + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Months day 27') + + def test_yearly_count(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'yearly', + 'interval': 2, + 'count': 3, + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Years for 3 events') + + def test_yearly_until(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'yearly', + 'interval': 2, + 'end_type': 'end_date', + 'until': datetime(2024, 11, 15), + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Years until 2024-11-15') + + def test_yearly_none(self): + recurrence = self.env['calendar.recurrence'].create({ + 'rrule_type': 'yearly', + 'interval': 2, + 'end_type': '', + 'event_tz': 'UTC', + }) + self.assertEqual(recurrence.name, 'Every 2 Years')