[FIX] calendar: translate recurrence rule
Steps to reproduce:
1. install the calendar module
2. in General settings, add the French -or any- language
3. create a new event
4. under **Options** tab choose the recurrence type to be weekly
5. select the days to repeat the event on
6. save the event
7. send an email using the **EMAIL** button, you can notice that works like (Weeks) and the days’ names are not translated but still in English
Investigation
- the translation is applied on the model level, not on the code level. So we have to explicitly/programmatically translate the options.
Discussion
- In the proposed fix, we compute the recurrence rule `name` by translating it to the current language -the event creator's language- but as this field is **stored**, it resulted in another issue that the invitation emails sent to the participants had a mix of languages: the email template being translated in each participant's language while the recurrence rule itself is translated in the owner's language being a **stored** field
- That's why the fix got extended to embed translating of the recurrence rule directly into the template.
- However there's a **_limitation_** we couldn't deal with that's there are two types of emails we are concerned with:
- invitation emails that follows `calendar attendee` model which works correctly as discussed
- update emails that follows `calendar event` model coming from the composer which deosn't allow to properly use partners/participants languages
Resolution
- The update emails are sent in the owner's language, with the intention to enhance the behavior in master (Task-3677327)
opw-3483319
closes odoo/odoo#151669
X-original-commit: 1d6d12ab67ffc96719c3c329cdba1cc3e3471f36
Signed-off-by: Arnaud Joset (arj) <arj@odoo.com>
Signed-off-by: Ali Hassan Youssef (alhy) <alhy@odoo.com>
This commit is contained in:
@@ -74,7 +74,7 @@
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="recurrent">
|
||||
<li>When: <t t-out="object.recurrence_id.name or ''">Every 1 Weeks, for 3 events</t></li>
|
||||
<li>When: <t t-out="object.recurrence_id.get_recurrence_name()">Every 1 Weeks, for 3 events</t></li>
|
||||
</t>
|
||||
<t t-if="not object.event_id.allday and object.event_id.duration">
|
||||
<li>Duration: <t t-out="('%dH%02d' % (object.event_id.duration,round(object.event_id.duration*60)%60)) or ''">0H30</t></li>
|
||||
@@ -205,7 +205,7 @@
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="recurrent">
|
||||
<li>When: <t t-out="object.recurrence_id.name or ''">Every 1 Weeks, for 3 events</t></li>
|
||||
<li>When: <t t-out="object.recurrence_id.get_recurrence_name()">Every 1 Weeks, for 3 events</t></li>
|
||||
</t>
|
||||
<t t-if="not object.event_id.allday and object.event_id.duration">
|
||||
<li>Duration: <t t-out="('%dH%02d' % (object.event_id.duration,round(object.event_id.duration*60)%60)) or ''">0H30</t></li>
|
||||
@@ -312,7 +312,7 @@
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="recurrent">
|
||||
<li>When: <t t-out="object.recurrence_id.name or ''">Every 1 Weeks, for 3 events</t></li>
|
||||
<li>When: <t t-out="object.recurrence_id.get_recurrence_name()">Every 1 Weeks, for 3 events</t></li>
|
||||
</t>
|
||||
<t t-if="not object.event_id.allday and object.event_id.duration">
|
||||
<li>Duration: <t t-out="('%dH%02d' % (object.event_id.duration,round(object.event_id.duration*60)%60)) or ''">0H30</t></li>
|
||||
@@ -423,7 +423,7 @@
|
||||
</li>
|
||||
</t>
|
||||
<t t-if="recurrent">
|
||||
<li>When: <t t-out="object.recurrence_id.name or ''">Every 1 Weeks, for 3 events</t></li>
|
||||
<li>When: <t t-out="object.recurrence_id.get_recurrence_name()">Every 1 Weeks, for 3 events</t></li>
|
||||
</t>
|
||||
<t t-if="not object.allday and object.duration">
|
||||
<li>Duration:
|
||||
|
||||
+242
-140
@@ -128,7 +128,7 @@ msgid ""
|
||||
" </li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"recurrent\">\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.name or ''\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.get_recurrence_name()\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"not object.event_id.allday and object.event_id.duration\">\n"
|
||||
" <li>Duration: <t t-out=\"('%dH%02d' % (object.event_id.duration,round(object.event_id.duration*60)%60)) or ''\">0H30</t></li>\n"
|
||||
@@ -170,110 +170,6 @@ msgid ""
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar
|
||||
#: model:mail.template,body_html:calendar.calendar_template_meeting_invitation
|
||||
msgid ""
|
||||
"<div>\n"
|
||||
" <t t-set=\"colors\" t-value=\"{'needsAction': 'grey', 'accepted': 'green', 'tentative': '#FFFF00', 'declined': 'red'}\"></t>\n"
|
||||
" <t t-set=\"customer\" t-value=\" object.event_id.find_partner_customer()\"></t>\n"
|
||||
" <t t-set=\"target_responsible\" t-value=\"object.partner_id == object.event_id.partner_id\"></t>\n"
|
||||
" <t t-set=\"target_customer\" t-value=\"object.partner_id == customer\"></t>\n"
|
||||
" <t t-set=\"recurrent\" t-value=\"object.recurrence_id and not ctx.get('calendar_template_ignore_recurrence')\"></t>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" Hello <t t-out=\"object.common_name or ''\">Wood Corner</t>,<br><br>\n"
|
||||
"\n"
|
||||
" <t t-if=\"not target_responsible\">\n"
|
||||
" <t t-out=\"object.event_id.user_id.partner_id.name or ''\">Colleen Diaz</t> invited you for the <strong t-out=\"object.event_id.name or ''\">Follow-up for Project proposal</strong> meeting.\n"
|
||||
" </t>\n"
|
||||
" <t t-else=\"\">\n"
|
||||
" Your meeting <strong t-out=\"object.event_id.name or ''\">Follow-up for Project proposal</strong> has been booked.\n"
|
||||
" </t>\n"
|
||||
"\n"
|
||||
" </p>\n"
|
||||
" <div style=\"text-align: center; padding: 16px 0px 16px 0px;\">\n"
|
||||
" <a t-attf-href=\"/calendar/meeting/accept?token={{object.access_token}}&id={{object.event_id.id}}\" style=\"padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px\">\n"
|
||||
" Accept</a>\n"
|
||||
" <a t-attf-href=\"/calendar/meeting/decline?token={{object.access_token}}&id={{object.event_id.id}}\" style=\"padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px\">\n"
|
||||
" Decline</a>\n"
|
||||
" <a t-attf-href=\"/calendar/meeting/view?token={{object.access_token}}&id={{object.event_id.id}}\" style=\"padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px\">View</a>\n"
|
||||
" </div>\n"
|
||||
" <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n"
|
||||
" <td width=\"130px;\" style=\"min-width: 130px;\">\n"
|
||||
" <div style=\"border-top-start-radius: 3px; border-top-end-radius: 3px; font-size: 12px; border-collapse: separate; text-align: center; font-weight: bold; color: #ffffff; min-height: 18px; background-color: #875A7B; border: 1px solid #875A7B;\">\n"
|
||||
" <t t-out=\"format_datetime(dt=object.event_id.start, tz=object.mail_tz if not object.event_id.allday else None, dt_format='EEEE', lang_code=object.env.lang) or ''\">Tuesday</t>\n"
|
||||
" </div>\n"
|
||||
" <div style=\"font-size: 48px; min-height: auto; font-weight: bold; text-align: center; color: #5F5F5F; background-color: #F8F8F8; border: 1px solid #875A7B;\">\n"
|
||||
" <t t-out=\"format_datetime(dt=object.event_id.start, tz=object.mail_tz if not object.event_id.allday else None, dt_format='d', lang_code=object.env.lang) or ''\">4</t>\n"
|
||||
" </div>\n"
|
||||
" <div style=\"font-size: 12px; text-align: center; font-weight: bold; color: #ffffff; background-color: #875A7B;\">\n"
|
||||
" <t t-out=\"format_datetime(dt=object.event_id.start, tz=object.mail_tz if not object.event_id.allday else None, dt_format='MMMM y', lang_code=object.env.lang) or ''\">May 2021</t>\n"
|
||||
" </div>\n"
|
||||
" <div style=\"border-collapse: separate; color: #5F5F5F; text-align: center; font-size: 12px; border-bottom-end-radius: 3px; font-weight: bold ; border: 1px solid #875A7B; border-bottom-start-radius: 3px;\">\n"
|
||||
" <t t-if=\"not object.event_id.allday\">\n"
|
||||
" <div>\n"
|
||||
" <t t-out=\"format_time(time=object.event_id.start, tz=object.mail_tz, time_format='short', lang_code=object.env.lang) or ''\">11:00 AM</t>\n"
|
||||
" </div>\n"
|
||||
" <t t-if=\"object.mail_tz\">\n"
|
||||
" <div style=\"font-size: 10px; font-weight: normal\">\n"
|
||||
" (<t t-out=\"object.mail_tz or ''\">Europe/Brussels</t>)\n"
|
||||
" </div>\n"
|
||||
" </t>\n"
|
||||
" </t>\n"
|
||||
" </div>\n"
|
||||
" </td>\n"
|
||||
" <td width=\"20px;\"></td>\n"
|
||||
" <td style=\"padding-top: 5px;\">\n"
|
||||
" <p><strong>Details of the event</strong></p>\n"
|
||||
" <ul>\n"
|
||||
" <t t-if=\"object.event_id.location\">\n"
|
||||
" <li>Location: <t t-out=\"object.event_id.location or ''\">Bruxelles</t>\n"
|
||||
" (<a target=\"_blank\" t-attf-href=\"http://maps.google.com/maps?oi=map&q={{object.event_id.location}}\">View Map</a>)\n"
|
||||
" </li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"recurrent\">\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.name or ''\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"not object.event_id.allday and object.event_id.duration\">\n"
|
||||
" <li>Duration: <t t-out=\"('%dH%02d' % (object.event_id.duration,round(object.event_id.duration*60)%60)) or ''\">0H30</t></li>\n"
|
||||
" </t>\n"
|
||||
" <li>Attendees\n"
|
||||
" <ul>\n"
|
||||
" <li t-foreach=\"object.event_id.attendee_ids\" t-as=\"attendee\">\n"
|
||||
" <div t-attf-style=\"display: inline-block; border-radius: 50%; width: 10px; height: 10px; background:{{ colors.get(attendee.state) or 'white' }};\"> </div>\n"
|
||||
" <t t-if=\"attendee.common_name != object.common_name\">\n"
|
||||
" <span style=\"margin-left:5px\" t-out=\"attendee.common_name or ''\">Mitchell Admin</span>\n"
|
||||
" </t>\n"
|
||||
" <t t-else=\"\">\n"
|
||||
" <span style=\"margin-left:5px\">You</span>\n"
|
||||
" </t>\n"
|
||||
" </li>\n"
|
||||
" </ul></li>\n"
|
||||
" <t t-if=\"object.event_id.videocall_location\">\n"
|
||||
" <li>\n"
|
||||
" How to Join:\n"
|
||||
" <t t-if=\"object.get_base_url() in object.event_id.videocall_location\"> Join with Odoo Discuss</t>\n"
|
||||
" <t t-else=\"\"> Join at</t><br>\n"
|
||||
" <a t-att-href=\"object.event_id.videocall_location\" target=\"_blank\" t-out=\"object.event_id.videocall_location or ''\">www.mycompany.com/calendar/join_videocall/xyz</a>\n"
|
||||
" </li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"not is_html_empty(object.event_id.description)\">\n"
|
||||
" <li>Description of the event:\n"
|
||||
" <t t-out=\"object.event_id.description\">Internal meeting for discussion for new pricing for product and services.</t></li>\n"
|
||||
" </t>\n"
|
||||
" </ul>\n"
|
||||
" </td>\n"
|
||||
" </tr></table>\n"
|
||||
" <br>\n"
|
||||
" Thank you,\n"
|
||||
" <t t-if=\"object.event_id.user_id.signature\">\n"
|
||||
" <br>\n"
|
||||
" <t t-out=\"object.event_id.user_id.signature or ''\">--<br>Mitchell Admin</t>\n"
|
||||
" </t>\n"
|
||||
"</div>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar
|
||||
#: model:mail.template,body_html:calendar.calendar_template_meeting_update
|
||||
msgid ""
|
||||
@@ -334,7 +230,7 @@ msgid ""
|
||||
" </li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"recurrent\">\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.name or ''\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.get_recurrence_name()\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"not object.allday and object.duration\">\n"
|
||||
" <li>Duration:\n"
|
||||
@@ -359,6 +255,141 @@ msgid ""
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar
|
||||
#: model:mail.template,body_html:calendar.calendar_template_meeting_invitation
|
||||
msgid ""
|
||||
"<div>\n"
|
||||
" <t t-set=\"colors\" t-value=\"{'needsAction': 'grey', 'accepted': 'green', 'tentative': '#FFFF00', 'declined': 'red'}\"></t>\n"
|
||||
" <t t-set=\"is_online\" t-value=\"'appointment_type_id' in object.event_id and object.event_id.appointment_type_id\"></t>\n"
|
||||
" <t t-set=\"customer\" t-value=\" object.event_id.find_partner_customer()\"></t>\n"
|
||||
" <t t-set=\"target_responsible\" t-value=\"object.partner_id == object.event_id.partner_id\"></t>\n"
|
||||
" <t t-set=\"target_customer\" t-value=\"object.partner_id == customer\"></t>\n"
|
||||
" <t t-set=\"recurrent\" t-value=\"object.recurrence_id and not ctx.get('calendar_template_ignore_recurrence')\"></t>\n"
|
||||
"\n"
|
||||
" <p>\n"
|
||||
" Hello <t t-out=\"object.common_name or ''\">Wood Corner</t>,<br><br>\n"
|
||||
"\n"
|
||||
" <t t-if=\"is_online and target_customer\">\n"
|
||||
" Your appointment <strong t-out=\"object.event_id.appointment_type_id.name or ''\">Schedule a Demo</strong> <t t-if=\"object.event_id.appointment_type_id.category != 'custom'\"> with <t t-out=\"object.event_id.user_id.name or ''\">Ready Mat</t></t> has been booked.\n"
|
||||
" <t t-if=\"is_online and object.state != 'accepted' and object.event_id.appointment_type_id.resource_manual_confirmation\">\n"
|
||||
" You will receive a mail of confirmation with more details when your appointment will be confirmed.\n"
|
||||
" </t>\n"
|
||||
" </t>\n"
|
||||
" <t t-elif=\"is_online and target_responsible\">\n"
|
||||
" <t t-if=\"customer\">\n"
|
||||
" <t t-out=\"customer.name or ''\"></t> scheduled the following appointment <strong t-out=\"object.event_id.appointment_type_id.name or ''\">Schedule a Demo</strong> with you.\n"
|
||||
" </t>\n"
|
||||
" <t t-else=\"\">\n"
|
||||
" Your appointment <strong t-out=\"object.event_id.appointment_type_id.name or ''\">Schedule a Demo</strong> has been booked.\n"
|
||||
" </t>\n"
|
||||
" </t>\n"
|
||||
" <t t-elif=\"not target_responsible\">\n"
|
||||
" <t t-out=\"object.event_id.user_id.partner_id.name or ''\">Colleen Diaz</t> invited you for the <strong t-out=\"object.event_id.name or ''\">Follow-up for Project proposal</strong> meeting.\n"
|
||||
" </t>\n"
|
||||
" <t t-else=\"\">\n"
|
||||
" Your meeting <strong t-out=\"object.event_id.name or ''\">Follow-up for Project proposal</strong> has been booked.\n"
|
||||
" </t>\n"
|
||||
"\n"
|
||||
" </p>\n"
|
||||
" <div style=\"text-align: center; padding: 16px 0px 16px 0px;\">\n"
|
||||
" <t t-if=\"not is_online or object.state != 'accepted'\">\n"
|
||||
" <a t-attf-href=\"/calendar/meeting/accept?token={{object.access_token}}&id={{object.event_id.id}}\" style=\"padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px\">\n"
|
||||
" Accept</a>\n"
|
||||
" <a t-attf-href=\"/calendar/meeting/decline?token={{object.access_token}}&id={{object.event_id.id}}\" style=\"padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px\">\n"
|
||||
" Decline</a>\n"
|
||||
" </t>\n"
|
||||
" <a t-attf-href=\"/calendar/meeting/view?token={{object.access_token}}&id={{object.event_id.id}}\" style=\"padding: 5px 10px; color: #FFFFFF; text-decoration: none; background-color: #875A7B; border: 1px solid #875A7B; border-radius: 3px\"><t t-out=\"'Reschedule' if is_online and target_customer else 'View'\">View</t></a>\n"
|
||||
" </div>\n"
|
||||
" <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>\n"
|
||||
" <td width=\"130px;\" style=\"min-width: 130px;\">\n"
|
||||
" <div style=\"border-top-start-radius: 3px; border-top-end-radius: 3px; font-size: 12px; border-collapse: separate; text-align: center; font-weight: bold; color: #ffffff; min-height: 18px; background-color: #875A7B; border: 1px solid #875A7B;\">\n"
|
||||
" <t t-out=\"format_datetime(dt=object.event_id.start, tz=object.mail_tz if not object.event_id.allday else None, dt_format='EEEE', lang_code=object.env.lang) or ''\">Tuesday</t>\n"
|
||||
" </div>\n"
|
||||
" <div style=\"font-size: 48px; min-height: auto; font-weight: bold; text-align: center; color: #5F5F5F; background-color: #F8F8F8; border: 1px solid #875A7B;\">\n"
|
||||
" <t t-out=\"str(object.event_id.start.day) or ''\">4</t>\n"
|
||||
" </div>\n"
|
||||
" <div style=\"font-size: 12px; text-align: center; font-weight: bold; color: #ffffff; background-color: #875A7B;\">\n"
|
||||
" <t t-out=\"format_datetime(dt=object.event_id.start, tz=object.mail_tz if not object.event_id.allday else None, dt_format='MMMM y', lang_code=object.env.lang) or ''\">May 2021</t>\n"
|
||||
" </div>\n"
|
||||
" <div style=\"border-collapse: separate; color: #5F5F5F; text-align: center; font-size: 12px; border-bottom-end-radius: 3px; font-weight: bold ; border: 1px solid #875A7B; border-bottom-start-radius: 3px;\">\n"
|
||||
" <t t-if=\"not object.event_id.allday\">\n"
|
||||
" <div>\n"
|
||||
" <t t-out=\"format_time(time=object.event_id.start, tz=object.mail_tz, time_format='short', lang_code=object.env.lang) or ''\">11:00 AM</t>\n"
|
||||
" </div>\n"
|
||||
" <t t-if=\"object.mail_tz\">\n"
|
||||
" <div style=\"font-size: 10px; font-weight: normal\">\n"
|
||||
" (<t t-out=\"object.mail_tz or ''\">Europe/Brussels</t>)\n"
|
||||
" </div>\n"
|
||||
" </t>\n"
|
||||
" </t>\n"
|
||||
" </div>\n"
|
||||
" </td>\n"
|
||||
" <td width=\"20px;\"></td>\n"
|
||||
" <td style=\"padding-top: 5px;\">\n"
|
||||
" <p><strong>Details of the event</strong></p>\n"
|
||||
" <ul>\n"
|
||||
" <t t-if=\"is_online\">\n"
|
||||
" <li>Appointment Type: <t t-out=\"object.event_id.appointment_type_id.name or ''\">Schedule a Demo</t></li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"object.event_id.location\">\n"
|
||||
" <li>Location: <t t-out=\"object.event_id.location or ''\">Bruxelles</t>\n"
|
||||
" (<a target=\"_blank\" t-attf-href=\"http://maps.google.com/maps?oi=map&q={{object.event_id.location}}\">View Map</a>)\n"
|
||||
" </li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"recurrent\">\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.get_recurrence_name()\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"not object.event_id.allday and object.event_id.duration\">\n"
|
||||
" <li>Duration: <t t-out=\"('%dH%02d' % (object.event_id.duration,round(object.event_id.duration*60)%60)) or ''\">0H30</t></li>\n"
|
||||
" </t>\n"
|
||||
" <li>Attendees\n"
|
||||
" <ul>\n"
|
||||
" <li t-foreach=\"object.event_id.attendee_ids\" t-as=\"attendee\">\n"
|
||||
" <div t-attf-style=\"display: inline-block; border-radius: 50%; width: 10px; height: 10px; background:{{ colors.get(attendee.state) or 'white' }};\"> </div>\n"
|
||||
" <t t-if=\"attendee.common_name != object.common_name\">\n"
|
||||
" <span style=\"margin-left:5px\" t-out=\"attendee.common_name or ''\">Mitchell Admin</span>\n"
|
||||
" </t>\n"
|
||||
" <t t-else=\"\">\n"
|
||||
" <span style=\"margin-left:5px\">You</span>\n"
|
||||
" </t>\n"
|
||||
" </li>\n"
|
||||
" </ul></li>\n"
|
||||
" <li t-if=\"is_online and object.event_id.appointment_type_id.resource_manage_capacity\">\n"
|
||||
" For: <t t-out=\"object.event_id.resource_total_capacity_reserved\"></t> people\n"
|
||||
" </li>\n"
|
||||
" <li t-if=\"is_online and object.event_id.appointment_type_id.assign_method != 'time_auto_assign'\">\n"
|
||||
" Resources\n"
|
||||
" <ul>\n"
|
||||
" <li t-foreach=\"object.event_id.appointment_resource_ids\" t-as=\"resource\">\n"
|
||||
" <span style=\"margin-left:5px\" t-out=\"resource.name or ''\">Table 1</span>\n"
|
||||
" </li>\n"
|
||||
" </ul>\n"
|
||||
" </li>\n"
|
||||
" <t t-if=\"object.event_id.videocall_location\">\n"
|
||||
" <li>\n"
|
||||
" How to Join:\n"
|
||||
" <t t-if=\"object.get_base_url() in object.event_id.videocall_location\"> Join with Odoo Discuss</t>\n"
|
||||
" <t t-else=\"\"> Join at</t><br>\n"
|
||||
" <a t-att-href=\"object.event_id.videocall_location\" target=\"_blank\" t-out=\"object.event_id.videocall_location or ''\">www.mycompany.com/calendar/join_videocall/xyz</a>\n"
|
||||
" </li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"not is_html_empty(object.event_id.description)\">\n"
|
||||
" <li>Description of the event:\n"
|
||||
" <t t-out=\"object.event_id.description\">Internal meeting for discussion for new pricing for product and services.</t></li>\n"
|
||||
" </t>\n"
|
||||
" </ul>\n"
|
||||
" </td>\n"
|
||||
" </tr></table>\n"
|
||||
" <br>\n"
|
||||
" Thank you,\n"
|
||||
" <t t-if=\"object.event_id.user_id.signature\">\n"
|
||||
" <br>\n"
|
||||
" <t t-out=\"object.event_id.user_id.signature or ''\">--<br>Mitchell Admin</t>\n"
|
||||
" </t>\n"
|
||||
"</div>\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#. module: calendar
|
||||
#: model:mail.template,body_html:calendar.calendar_template_meeting_reminder
|
||||
msgid ""
|
||||
@@ -412,7 +443,7 @@ msgid ""
|
||||
" </li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"recurrent\">\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.name or ''\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" <li>When: <t t-out=\"object.recurrence_id.get_recurrence_name()\">Every 1 Weeks, for 3 events</t></li>\n"
|
||||
" </t>\n"
|
||||
" <t t-if=\"not object.event_id.allday and object.event_id.duration\">\n"
|
||||
" <li>Duration: <t t-out=\"('%dH%02d' % (object.event_id.duration,round(object.event_id.duration*60)%60)) or ''\">0H30</t></li>\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,"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
Reference in New Issue
Block a user