diff --git a/docsource/modules190-200.rst b/docsource/modules190-200.rst index c5ca3821e8e..e7745d52f8f 100644 --- a/docsource/modules190-200.rst +++ b/docsource/modules190-200.rst @@ -106,9 +106,9 @@ Module coverage 19.0 -> 20.0 +---------------------------------------------------+----------------------+-------------------------------------------------+ | bus |Nothing to do |No schema change. im_status is not stored. | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| calendar | | | +| calendar |Done | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| calendar_sms | | | +| calendar_sms |Nothing to do | | +---------------------------------------------------+----------------------+-------------------------------------------------+ | certificate | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ @@ -766,11 +766,11 @@ Module coverage 19.0 -> 20.0 +---------------------------------------------------+----------------------+-------------------------------------------------+ | mail_plugin | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| |new| mail_tracking |Done |Installed from mail; history kept. | +| |new| mail_tracking | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| |new| mail_tracking_mass_mailing |Nothing to do |New field. Installed with mail_tracking. | +| |new| mail_tracking_mass_mailing | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| |new| mail_tracking_sms |Nothing to do |New field. Installed with mail_tracking. | +| |new| mail_tracking_sms | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ | maintenance | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ @@ -1078,9 +1078,9 @@ Module coverage 19.0 -> 20.0 +---------------------------------------------------+----------------------+-------------------------------------------------+ | repair | | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| resource | | | +| resource |Done | | +---------------------------------------------------+----------------------+-------------------------------------------------+ -| resource_mail | | | +| resource_mail |Nothing to do |color moved to resource. | +---------------------------------------------------+----------------------+-------------------------------------------------+ | rpc | |No DB layout changes. | +---------------------------------------------------+----------------------+-------------------------------------------------+ diff --git a/openupgrade_scripts/scripts/calendar/20.0.1.1/noupdate_changes.xml b/openupgrade_scripts/scripts/calendar/20.0.1.1/noupdate_changes.xml new file mode 100644 index 00000000000..bc94ffadcf9 --- /dev/null +++ b/openupgrade_scripts/scripts/calendar/20.0.1.1/noupdate_changes.xml @@ -0,0 +1,562 @@ + + + + +
+ + + +

+ Event canceled + +

+

+ Hello,
+ + This is to inform you that the event Follow-up for Project proposal organized by Colleen Diaz has been canceled and removed from your calendar. + +

+
+

Details

+ + + + + + + + + + + + + +
Date & Time + Tuesday + 4 + May 2021 + + 11:00 AM + + + (Europe/Brussels) + + + +
Duration + 0H30 +
Location + Bruxelles +
+
+

+ If you have any questions or concerns, please feel free to contact us.
+ Best regards, + The Calendar Team +

+
+
+ Calendar: Event Canceled + Canceled event: {{ object.name }} + mail.mail_notification_light +
+ + +
+ + + + + + +

+ Date Updated + +

+

+ Hello Ready Mat, + + Your appointment with Jesse Brown has been updated: + + Your appointment has been updated: + Schedule a Demo is now scheduled for + 05/04/2021 at (11:00:00 To 11:30:00) (Europe/Brussels). + + + Your appointment Schedule a Demo with Colleen Diaz + is now scheduled for 05/04/2021 at (11:00:00 To 11:30:00) (Europe/Brussels). + + + The date of the meeting Follow-up for Project proposal created by Colleen Diaz is now scheduled for + 05/04/2021 at (11:00:00 To 11:30:00) (Europe/Brussels). + +

+
+ + View +
+
+

Details

+ + + + + + + + + + + + + + + + + + + + + +
Date & Time + Tuesday + 4 + May 2021 + + 11:00 AM + + + (Europe/Brussels) + + + +
When + Every 1 Weeks, for 3 events +
Duration + 0H30 +
Location + Bruxelles + View Map +
+ Join with + Join + + + Odoo Discuss + Video meeting + +
+
+
+ +

Attendees

+
+ + + Mitchell Admin + + + You + +
+
+ ... and 2 more +
+
+
+

Description of the event

+ Internal meeting for discussion for new pricing for product and services. +
+
+ Add to calendar + + + + + + +
+ + iCal + + + + Outlook + + + + Google + +
+

+ Thank you! + Mitchell Admin +

+
+
+ mail.mail_notification_light +
+ + +
+ + + + +

+ Invitation +

+

+ Hello Wood Corner, + + + You have been invited by Customer to the Follow-up for Project proposal meeting. + + Colleen Diaz invited you for the Follow-up for Project proposal meeting. + + + + Your meeting Follow-up for Project proposal has been booked. + +

+
+ + View +
+
+

Details

+ + + + + + + + + + + + + + + + + + + + + +
Date & Time + Tuesday + 4 + May 2021 + + 11:00 AM + + + (Europe/Brussels) + + + +
When + Every 1 Weeks, for 3 events +
Duration + 0H30 +
Location + Bruxelles + View Map +
+ Join with + Join + + + Odoo Discuss + Video meeting + +
+
+
+ +

Attendees

+
+ + + Mitchell Admin + + + You + +
+
+ ... and 2 more +
+
+
+

Description of the event

+ Internal meeting for discussion for new pricing for product and services. +
+
+ Add to calendar + + + + + + +
+ + iCal + + + + Outlook + + + + Google + +
+

+ Thank you! + Mitchell Admin +

+
+
+ mail.mail_notification_light +
+ + +
+ + +

+ Reminder + +

+

+ Hello Gemini Furniture,
+ This is a reminder for the event below. +

+
+ + View +
+
+

Details

+ + + + + + + + + + + + + + + + + + + + + +
Date & Time + Tuesday + 4 + May 2021 + + 11:00 AM + + + (Europe/Brussels) + + + +
When + Every 1 Weeks, for 3 events +
Duration + 0H30 +
Location + Bruxelles + View Map +
+ Join with + Join + + + Odoo Discuss + Video meeting + +
+
+
+ +

Attendees

+
+ + + Mitchell Admin + + + You + +
+
+ ... and 2 more +
+
+
+

Description of the event

+ Internal meeting for discussion for new pricing for product and services. +
+
+ Add to calendar + + + + + + +
+ + iCal + + + + Outlook + + + + Google + +
+

+ Thank you! + Mitchell Admin +

+
+
+ mail.mail_notification_light +
+ + +
+ + + +

+ Event updated + +

+

Hello, + This meeting has been updated. +

+
+

Details

+ + + + + + + + + + + + + + + + + + + + + +
Date & Time + Tuesday + 4 + May 2021 + + 11:00 AM + + + (Europe/Brussels) + + + +
When + Every 1 Weeks, for 3 events +
Duration + 0H30 +
Location + Bruxelles + View Map +
+ Join with + Join + + + Odoo Discuss + Video meeting + +
+
+
+ +

Attendees

+
+ + + Mitchell Admin + + + You + +
+
+ ... and 2 more +
+
+
+

Description of the event

+ Internal meeting for discussion for new pricing for product and services. +
+
+ Add to calendar + + + + + + +
+ + iCal + + + + Outlook + + + + Google + +
+

+ Thank you! + Mitchell Admin +

+
+
+ mail.mail_notification_light +
+ + Invitation Status + +
diff --git a/openupgrade_scripts/scripts/calendar/20.0.1.1/post-migration.py b/openupgrade_scripts/scripts/calendar/20.0.1.1/post-migration.py new file mode 100644 index 00000000000..04ab1c97618 --- /dev/null +++ b/openupgrade_scripts/scripts/calendar/20.0.1.1/post-migration.py @@ -0,0 +1,134 @@ +# Copyright 2026 Odoo Community Association (OCA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + +_PRIVACY_VALUES = ("public", "private", "confidential") + + +def _create_missing_primary_calendars(env): + """The post_init hook that creates primary calendars runs on install only. + + Schema init may already have created one for users who organise events + (calendar.event.calendar_id's compute calls _find_or_create_primary_calendar). + Everyone else who is not a portal/public user still needs one: v20 creates + it for every new internal user and refuses to delete it. + """ + users = ( + env["res.users"] + .with_context(active_test=False) + .search( + [ + ("share", "=", False), + ("calendar_user_ids", "not any", [("is_primary", "=", True)]), + ] + ) + ) + if users: + users._generate_primary_calendar() + openupgrade.logged_query( + env.cr, + """ + UPDATE res_users AS users + SET primary_calendar_id = membership.calendar_id + FROM calendar_user AS membership + WHERE membership.user_id = users.id + AND membership.is_primary + AND membership.access_role = 'owner' + AND users.primary_calendar_id IS NULL + """, + ) + + +def _restore_default_privacy(cr): + """calendar_default_privacy moved from res.users.settings to the calendar. + + _generate_primary_calendar copies the system parameter (or 'public'), + not the per-user value. The old column is still present: v20 no longer + drops removed columns. + """ + if not openupgrade.column_exists( + cr, "res_users_settings", "calendar_default_privacy" + ): + return + openupgrade.logged_query( + cr, + """ + UPDATE calendar_calendar AS calendar + SET calendar_default_privacy = settings.calendar_default_privacy + FROM calendar_user AS membership + JOIN res_users_settings AS settings + ON settings.user_id = membership.user_id + WHERE membership.calendar_id = calendar.id + AND membership.is_primary + AND membership.access_role = 'owner' + AND settings.calendar_default_privacy IN %s + AND calendar.calendar_default_privacy + IS DISTINCT FROM settings.calendar_default_privacy + """, + (_PRIVACY_VALUES,), + ) + + +def _assign_event_calendars(cr): + """Events belong to their organiser's primary calendar. + + The stored compute does this when it runs. Fill whatever it left empty + (no organiser, or a calendar created only in this post script). + """ + openupgrade.logged_query( + cr, + """ + UPDATE calendar_event AS event + SET calendar_id = users.primary_calendar_id + FROM res_users AS users + WHERE event.user_id = users.id + AND event.calendar_id IS NULL + AND users.primary_calendar_id IS NOT NULL + """, + ) + + +def _restore_own_filter_checked(cr): + """The user's own events are filtered by calendar.user.is_filter_checked. + + v19 stored that checkbox on calendar.filters.partner_checked of the + user's own partner. Other partners were folded into ``active`` in pre. + """ + if not openupgrade.column_exists(cr, "calendar_filters", "partner_checked"): + return + openupgrade.logged_query( + cr, + """ + UPDATE calendar_user AS membership + SET is_filter_checked = FALSE + FROM res_users AS users + JOIN calendar_filters AS filters + ON filters.user_id = users.id + AND filters.partner_id = users.partner_id + WHERE membership.user_id = users.id + AND membership.is_primary + AND filters.partner_checked IS FALSE + """, + ) + + +@openupgrade.migrate() +def migrate(env, version): + _create_missing_primary_calendars(env) + _restore_default_privacy(env.cr) + _assign_event_calendars(env.cr) + _restore_own_filter_checked(env.cr) + openupgrade.load_data(env, "calendar", "20.0.1.1/noupdate_changes.xml") + openupgrade.delete_record_translations( + env.cr, + "calendar", + [ + "calendar_template_delete_event", + "calendar_template_meeting_changedate", + "calendar_template_meeting_invitation", + "calendar_template_meeting_reminder", + "calendar_template_meeting_update", + "subtype_invitation", + ], + ) diff --git a/openupgrade_scripts/scripts/calendar/20.0.1.1/pre-migration.py b/openupgrade_scripts/scripts/calendar/20.0.1.1/pre-migration.py new file mode 100644 index 00000000000..e422837ce9b --- /dev/null +++ b/openupgrade_scripts/scripts/calendar/20.0.1.1/pre-migration.py @@ -0,0 +1,67 @@ +# Copyright 2026 Odoo Community Association (OCA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + + +def _merge_partner_filters(cr): + """v20 keeps a single ``active`` flag on calendar.filters. + + v19 used ``active`` for "listed" and ``partner_checked`` for "shown". + The shown state is what hides or displays that partner's events, so an + unchecked filter becomes inactive. The user's own events are filtered + through calendar.user in post-migration. + """ + if not openupgrade.column_exists(cr, "calendar_filters", "partner_checked"): + return + openupgrade.logged_query( + cr, + """ + UPDATE calendar_filters + SET active = FALSE + WHERE partner_checked IS FALSE + AND active IS DISTINCT FROM FALSE + """, + ) + + +def _deduplicate_access_tokens(cr): + """v20 adds UNIQUE(access_token). Several NULL tokens are allowed; + duplicate non-null tokens are not. Keep the oldest row's token so + existing links to that event keep working. + """ + openupgrade.logged_query( + cr, + """ + UPDATE calendar_event + SET access_token = NULL + WHERE access_token = '' + """, + ) + openupgrade.logged_query( + cr, + """ + UPDATE calendar_event AS event + SET access_token = gen_random_uuid()::varchar + WHERE event.id IN ( + SELECT id + FROM ( + SELECT + id, + ROW_NUMBER() OVER ( + PARTITION BY access_token + ORDER BY id + ) AS occurrence + FROM calendar_event + WHERE access_token IS NOT NULL + ) AS ranked + WHERE occurrence > 1 + ) + """, + ) + + +@openupgrade.migrate() +def migrate(env, version): + _merge_partner_filters(env.cr) + _deduplicate_access_tokens(env.cr) diff --git a/openupgrade_scripts/scripts/calendar/20.0.1.1/upgrade_analysis.txt b/openupgrade_scripts/scripts/calendar/20.0.1.1/upgrade_analysis.txt new file mode 100644 index 00000000000..3cf0115c297 --- /dev/null +++ b/openupgrade_scripts/scripts/calendar/20.0.1.1/upgrade_analysis.txt @@ -0,0 +1,96 @@ +---Models in module 'calendar'--- +obsolete model calendar.popover.delete.wizard [transient] +obsolete model calendar.provider.config [transient] +new model calendar.calendar +new model calendar.event.archive.or.unlink.wizard [transient] +new model calendar.event.multi.archive.or.unlink.wizard [transient] +new model calendar.user +---Fields in module 'calendar'--- +calendar / calendar.calendar / calendar_default_privacy (selection): NEW required, selection_keys: ['confidential', 'members_only', 'private', 'public'], hasdefault: default +calendar / calendar.calendar / calendar_user_ids (one2many) : NEW relation: calendar.user, hasdefault: default +calendar / calendar.calendar / event_ids (one2many) : NEW relation: calendar.event +calendar / calendar.calendar / name (char) : NEW +calendar / calendar.calendar / recurrence_ids (one2many) : NEW relation: calendar.recurrence +calendar / calendar.event / calendar_id (many2one) : NEW relation: calendar.calendar, hasdefault: compute +calendar / calendar.event / effective_privacy (selection) : selection_keys added: [members_only] (most likely nothing to do) +calendar / calendar.event / is_draft (boolean) : NEW +calendar / calendar.event / meeting_activity_ids (one2many): NEW relation: mail.activity +calendar / calendar.event / privacy (selection) : selection_keys added: [members_only] (most likely nothing to do) +calendar / calendar.filters / partner_checked (boolean) : DEL +calendar / calendar.user / access_role (selection) : NEW required, selection_keys: ['owner', 'writer'] +calendar / calendar.user / calendar_id (many2one) : NEW relation: calendar.calendar, required +calendar / calendar.user / filter_color (integer) : NEW hasdefault: default +calendar / calendar.user / is_filter_active (boolean) : NEW hasdefault: default +calendar / calendar.user / is_filter_checked (boolean) : NEW hasdefault: default +calendar / calendar.user / is_primary (boolean) : NEW +calendar / calendar.user / name (char) : NEW +calendar / calendar.user / user_id (many2one) : NEW relation: res.users, required +calendar / res.users / activity_calendar_event_id (many2one): not related anymore +calendar / res.users / activity_calendar_event_id (many2one): now a function +calendar / res.users / calendar_ids (one2many) : NEW relation: calendar.calendar, hasdefault: compute, stored: False +calendar / res.users / calendar_user_ids (one2many) : NEW relation: calendar.user +calendar / res.users / primary_calendar_id (many2one): NEW relation: calendar.calendar, isfunction: function, stored +calendar / res.users.settings / calendar_default_privacy (selection): DEL required, selection_keys: ['confidential', 'private', 'public'] +calendar / res.users.settings / calendar_show_activities (boolean): NEW hasdefault: default +---XML records in module 'calendar'--- +NEW calendar.alarm: calendar.alarm_mail_1_day (noupdate) +NEW ir.access: calendar.access_calendar_alarm +NEW ir.access: calendar.access_calendar_alarm_manager +NEW ir.access: calendar.access_calendar_attendee_employee +NEW ir.access: calendar.access_calendar_attendee_employee_archive_or_unlink_wizard +NEW ir.access: calendar.access_calendar_attendee_employee_multi_archive_or_unlink_wizard +NEW ir.access: calendar.access_calendar_event_type_all +NEW ir.access: calendar.access_calendar_event_type_manager +NEW ir.access: calendar.access_calendar_filters_all +NEW ir.access: calendar.access_calendar_recurrence +NEW ir.access: calendar.calendar_calendar_rule_system +NEW ir.access: calendar.calendar_calendar_rule_users_create_and_read +NEW ir.access: calendar.calendar_calendar_rule_users_update +NEW ir.access: calendar.calendar_event_rule_employee +NEW ir.access: calendar.calendar_event_rule_my +NEW ir.access: calendar.calendar_event_rule_private +NEW ir.access: calendar.calendar_user_rule_create +NEW ir.access: calendar.calendar_user_rule_own_or_owner +NEW ir.access: calendar.calendar_user_rule_read +NEW ir.actions.act_window: calendar.action_calendar_calendar +DEL ir.actions.act_window: calendar.action_event_delete_wizard +DEL ir.model.access: calendar.access_calendar_alarm +DEL ir.model.access: calendar.access_calendar_alarm_manager +DEL ir.model.access: calendar.access_calendar_attendee_employee +DEL ir.model.access: calendar.access_calendar_attendee_employee_delete_wizard +DEL ir.model.access: calendar.access_calendar_attendee_portal +DEL ir.model.access: calendar.access_calendar_event_all_employee +DEL ir.model.access: calendar.access_calendar_event_all_user +DEL ir.model.access: calendar.access_calendar_event_partner_manager +DEL ir.model.access: calendar.access_calendar_event_type_all +DEL ir.model.access: calendar.access_calendar_event_type_manager +DEL ir.model.access: calendar.access_calendar_filters +DEL ir.model.access: calendar.access_calendar_filters_all +DEL ir.model.access: calendar.access_calendar_provider_config_all +DEL ir.model.access: calendar.access_calendar_provider_config_manager +DEL ir.model.access: calendar.access_calendar_recurrence +NEW ir.model.constraint: calendar.constraint_calendar_event_access_token_unique +NEW ir.model.constraint: calendar.constraint_calendar_event_check_no_recurrent_draft +NEW ir.model.constraint: calendar.constraint_calendar_user_single_primary_calendar_per_user +NEW ir.model.constraint: calendar.constraint_calendar_user_unique_user_per_calendar +DEL ir.rule: calendar.calendar_attendee_rule_my (noupdate) +DEL ir.rule: calendar.calendar_event_rule_employee (noupdate) +DEL ir.rule: calendar.calendar_event_rule_my (noupdate) +DEL ir.rule: calendar.calendar_event_rule_private (noupdate) +NEW ir.ui.menu: calendar.calendar_submenu_event_type +NEW ir.ui.menu: calendar.menu_calendar_calendars +NEW ir.ui.view: calendar.calendar_alarm_view_kanban +NEW ir.ui.view: calendar.calendar_alarm_view_search +NEW ir.ui.view: calendar.calendar_calendar_view_form +NEW ir.ui.view: calendar.calendar_calendar_view_list +NEW ir.ui.view: calendar.calendar_event_archive_or_unlink_wizard_view_form +NEW ir.ui.view: calendar.calendar_event_multi_archive_or_unlink_wizard_view_form +NEW ir.ui.view: calendar.calendar_user_view_form +NEW ir.ui.view: calendar.message_meeting_activity_done +NEW ir.ui.view: calendar.res_partner_view_form_simplified +NEW ir.ui.view: calendar.res_users_form_view_calendars +DEL ir.ui.view: calendar.calendar_popover_delete_view +DEL ir.ui.view: calendar.calendar_provider_config_view_form +DEL ir.ui.view: calendar.res_users_form_view_calendar_default_privacy +DEL ir.ui.view: calendar.view_event_delete_wizard_form +NEW mail.message.subtype: calendar.mt_calendar_event_updated (noupdate) diff --git a/openupgrade_scripts/scripts/calendar/20.0.1.1/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/calendar/20.0.1.1/upgrade_analysis_work.txt new file mode 100644 index 00000000000..f88742a229e --- /dev/null +++ b/openupgrade_scripts/scripts/calendar/20.0.1.1/upgrade_analysis_work.txt @@ -0,0 +1,161 @@ +---Models in module 'calendar'--- +obsolete model calendar.popover.delete.wizard [transient] +obsolete model calendar.provider.config [transient] +# NOTHING TO DO: transient wizards (delete popover, provider config). No business data. + +new model calendar.calendar +new model calendar.user +# DONE: post-migration creates a primary calendar.calendar and an owner +# calendar.user for every non-share user that does not already have one. +# calendar.event.calendar_id's compute may already have created those rows +# for organisers while the new column was initialised; the post script only +# fills the gaps. Odoo's post_init hook does the same job on a fresh install +# and does not run on upgrade (odoo/odoo@c03c350). + +new model calendar.event.archive.or.unlink.wizard [transient] +new model calendar.event.multi.archive.or.unlink.wizard [transient] +# NOTHING TO DO: new transient wizards. + +---Fields in module 'calendar'--- +calendar / calendar.calendar / calendar_default_privacy (selection): NEW required, selection_keys: ['confidential', 'members_only', 'private', 'public'], hasdefault: default +calendar / res.users.settings / calendar_default_privacy (selection): DEL required, selection_keys: ['confidential', 'private', 'public'] +# DONE: post-migration copies res_users_settings.calendar_default_privacy onto +# the user's primary calendar. The column is not dropped (Odoo 20 only drops +# NOT NULL on removed fields). Users with no settings row keep the value +# _generate_primary_calendar already stored (system parameter, else public), +# which is the v19 fallback. members_only is new and unused by old rows. + +calendar / calendar.calendar / calendar_user_ids (one2many) : NEW relation: calendar.user, hasdefault: default +calendar / calendar.calendar / event_ids (one2many) : NEW relation: calendar.event +calendar / calendar.calendar / name (char) : NEW +calendar / calendar.calendar / recurrence_ids (one2many) : NEW relation: calendar.recurrence +calendar / calendar.user / access_role (selection) : NEW required, selection_keys: ['owner', 'writer'] +calendar / calendar.user / calendar_id (many2one) : NEW relation: calendar.calendar, required +calendar / calendar.user / filter_color (integer) : NEW hasdefault: default +calendar / calendar.user / is_filter_active (boolean) : NEW hasdefault: default +calendar / calendar.user / is_filter_checked (boolean) : NEW hasdefault: default +calendar / calendar.user / is_primary (boolean) : NEW +calendar / calendar.user / name (char) : NEW +calendar / calendar.user / user_id (many2one) : NEW relation: res.users, required +# DONE: filled together with the primary calendar (access_role=owner, +# is_primary, filter defaults). See the new-model note above. +# is_filter_checked of the user's own partner is restored from +# calendar.filters.partner_checked when that flag was false. + +calendar / calendar.event / calendar_id (many2one) : NEW relation: calendar.calendar, hasdefault: compute +calendar / res.users / primary_calendar_id (many2one): NEW relation: calendar.calendar, isfunction: function, stored +# DONE: post-migration points each event at its organiser's primary calendar +# when the stored compute left it empty, and backfills primary_calendar_id +# from the owner membership. Events with no organiser stay without a calendar, +# which the field allows. calendar.recurrence.calendar_id is a non-stored +# related and is not in this analysis. + +calendar / calendar.event / effective_privacy (selection) : selection_keys added: [members_only] (most likely nothing to do) +calendar / calendar.event / privacy (selection) : selection_keys added: [members_only] (most likely nothing to do) +# NOTHING TO DO: new selection key. Existing rows stay public/private/confidential. +# effective_privacy is not stored. + +calendar / calendar.event / is_draft (boolean) : NEW +# NOTHING TO DO: existing meetings are not drafts. NULL satisfies +# CHECK(NOT (is_draft AND recurrency)) because NULL is not TRUE. + +calendar / calendar.event / meeting_activity_ids (one2many): NEW relation: mail.activity +# NOTHING TO DO: inverse of the existing mail.activity.calendar_event_id. No column. + +calendar / calendar.filters / partner_checked (boolean) : DEL +# DONE: pre-migration sets active=false where partner_checked is false, so +# partners the user had unchecked stay hidden. v20 has no separate "listed +# but unchecked" flag; that distinction cannot be kept. The user's own +# checkbox is applied to calendar.user.is_filter_checked in post-migration. + +calendar / res.users / activity_calendar_event_id (many2one): not related anymore +calendar / res.users / activity_calendar_event_id (many2one): now a function +# NOTHING TO DO: non-stored compute on mail.activity.mixin in both 19.0 and +# 20.0 (the implementation changed from next(iter) to activities[:1]). No column. + +calendar / res.users / calendar_ids (one2many) : NEW relation: calendar.calendar, hasdefault: compute, stored: False +calendar / res.users / calendar_user_ids (one2many) : NEW relation: calendar.user +# NOTHING TO DO: non-stored / one2many. Populated by the membership rows above. + +calendar / res.users.settings / calendar_show_activities (boolean): NEW hasdefault: default +# NOTHING TO DO: new preference, ORM default True. + +---XML records in module 'calendar'--- +NEW calendar.alarm: calendar.alarm_mail_1_day (noupdate) +NEW mail.message.subtype: calendar.mt_calendar_event_updated (noupdate) +# NOTHING TO DO: new noupdate records, loaded by the module update. + +NEW ir.access: calendar.access_calendar_alarm +NEW ir.access: calendar.access_calendar_alarm_manager +NEW ir.access: calendar.access_calendar_attendee_employee +NEW ir.access: calendar.access_calendar_attendee_employee_archive_or_unlink_wizard +NEW ir.access: calendar.access_calendar_attendee_employee_multi_archive_or_unlink_wizard +NEW ir.access: calendar.access_calendar_event_type_all +NEW ir.access: calendar.access_calendar_event_type_manager +NEW ir.access: calendar.access_calendar_filters_all +NEW ir.access: calendar.access_calendar_recurrence +NEW ir.access: calendar.calendar_calendar_rule_system +NEW ir.access: calendar.calendar_calendar_rule_users_create_and_read +NEW ir.access: calendar.calendar_calendar_rule_users_update +NEW ir.access: calendar.calendar_event_rule_employee +NEW ir.access: calendar.calendar_event_rule_my +NEW ir.access: calendar.calendar_event_rule_private +NEW ir.access: calendar.calendar_user_rule_create +NEW ir.access: calendar.calendar_user_rule_own_or_owner +NEW ir.access: calendar.calendar_user_rule_read +DEL ir.model.access: calendar.access_calendar_alarm +DEL ir.model.access: calendar.access_calendar_alarm_manager +DEL ir.model.access: calendar.access_calendar_attendee_employee +DEL ir.model.access: calendar.access_calendar_attendee_employee_delete_wizard +DEL ir.model.access: calendar.access_calendar_attendee_portal +DEL ir.model.access: calendar.access_calendar_event_all_employee +DEL ir.model.access: calendar.access_calendar_event_all_user +DEL ir.model.access: calendar.access_calendar_event_partner_manager +DEL ir.model.access: calendar.access_calendar_event_type_all +DEL ir.model.access: calendar.access_calendar_event_type_manager +DEL ir.model.access: calendar.access_calendar_filters +DEL ir.model.access: calendar.access_calendar_filters_all +DEL ir.model.access: calendar.access_calendar_provider_config_all +DEL ir.model.access: calendar.access_calendar_provider_config_manager +DEL ir.model.access: calendar.access_calendar_recurrence +DEL ir.rule: calendar.calendar_attendee_rule_my (noupdate) +DEL ir.rule: calendar.calendar_event_rule_employee (noupdate) +DEL ir.rule: calendar.calendar_event_rule_my (noupdate) +DEL ir.rule: calendar.calendar_event_rule_private (noupdate) +# NOTHING TO DO: ir.model.access and ir.rule were merged into ir.access by base. +# The base migration (OCA/OpenUpgrade#6068) renames the old xmlids and preserves +# manual rules. Standard records are recreated from this module's XML. + +NEW ir.actions.act_window: calendar.action_calendar_calendar +DEL ir.actions.act_window: calendar.action_event_delete_wizard +NEW ir.ui.menu: calendar.calendar_submenu_event_type +NEW ir.ui.menu: calendar.menu_calendar_calendars +NEW ir.ui.view: calendar.calendar_alarm_view_kanban +NEW ir.ui.view: calendar.calendar_alarm_view_search +NEW ir.ui.view: calendar.calendar_calendar_view_form +NEW ir.ui.view: calendar.calendar_calendar_view_list +NEW ir.ui.view: calendar.calendar_event_archive_or_unlink_wizard_view_form +NEW ir.ui.view: calendar.calendar_event_multi_archive_or_unlink_wizard_view_form +NEW ir.ui.view: calendar.calendar_user_view_form +NEW ir.ui.view: calendar.message_meeting_activity_done +NEW ir.ui.view: calendar.res_partner_view_form_simplified +NEW ir.ui.view: calendar.res_users_form_view_calendars +DEL ir.ui.view: calendar.calendar_popover_delete_view +DEL ir.ui.view: calendar.calendar_provider_config_view_form +DEL ir.ui.view: calendar.res_users_form_view_calendar_default_privacy +DEL ir.ui.view: calendar.view_event_delete_wizard_form +# NOTHING TO DO: views, menus and actions are loaded or removed by the module update. + +NEW ir.model.constraint: calendar.constraint_calendar_event_access_token_unique +# DONE: pre-migration nulls empty tokens and regenerates duplicate non-null +# tokens (lowest id keeps its token, so that event's links still work). + +NEW ir.model.constraint: calendar.constraint_calendar_event_check_no_recurrent_draft +NEW ir.model.constraint: calendar.constraint_calendar_user_single_primary_calendar_per_user +NEW ir.model.constraint: calendar.constraint_calendar_user_unique_user_per_calendar +# NOTHING TO DO: no drafts yet, and the post script creates exactly one primary +# owner membership per user. The unique indexes are added by the ORM before post. + +# noupdate_changes.xml (mail templates + subtype_invitation label) +# DONE: post-migration loads it and drops translations of those records so a +# stale translated body does not hide the rewritten English template. diff --git a/openupgrade_scripts/scripts/calendar_sms/20.0.1.1/upgrade_analysis.txt b/openupgrade_scripts/scripts/calendar_sms/20.0.1.1/upgrade_analysis.txt new file mode 100644 index 00000000000..68a9fff1adc --- /dev/null +++ b/openupgrade_scripts/scripts/calendar_sms/20.0.1.1/upgrade_analysis.txt @@ -0,0 +1,4 @@ +---Models in module 'calendar_sms'--- +---Fields in module 'calendar_sms'--- +---XML records in module 'calendar_sms'--- +NEW ir.ui.view: calendar_sms.calendar_alarm_view_kanban diff --git a/openupgrade_scripts/scripts/calendar_sms/20.0.1.1/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/calendar_sms/20.0.1.1/upgrade_analysis_work.txt new file mode 100644 index 00000000000..c232e2a65a7 --- /dev/null +++ b/openupgrade_scripts/scripts/calendar_sms/20.0.1.1/upgrade_analysis_work.txt @@ -0,0 +1,10 @@ +---Models in module 'calendar_sms'--- +# NOTHING TO DO + +---Fields in module 'calendar_sms'--- +# NOTHING TO DO + +---XML records in module 'calendar_sms'--- +NEW ir.ui.view: calendar_sms.calendar_alarm_view_kanban +# NOTHING TO DO: new view, loaded by the module update. No stored data changed. +# The v19 sms_notify_responsible rename lives in the 19.0 calendar scripts. diff --git a/openupgrade_scripts/scripts/resource/20.0.1.1/noupdate_changes.xml b/openupgrade_scripts/scripts/resource/20.0.1.1/noupdate_changes.xml new file mode 100644 index 00000000000..c96199ff946 --- /dev/null +++ b/openupgrade_scripts/scripts/resource/20.0.1.1/noupdate_changes.xml @@ -0,0 +1,6 @@ + + + + 40 hours/week + + diff --git a/openupgrade_scripts/scripts/resource/20.0.1.1/post-migration.py b/openupgrade_scripts/scripts/resource/20.0.1.1/post-migration.py new file mode 100644 index 00000000000..89f5b34dfb4 --- /dev/null +++ b/openupgrade_scripts/scripts/resource/20.0.1.1/post-migration.py @@ -0,0 +1,110 @@ +# Copyright 2026 Odoo Community Association (OCA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +from openupgradelib import openupgrade + +_logger = logging.getLogger(__name__) + + +def _convert_two_week_attendances(cr): + """Turn a two-week pattern into a variable calendar. + + v19 week_type follows ``floor((date.toordinal() - 1) / 7) % 2``, so a + given weekday keeps its week type every 14 days. Each work line becomes + one attendance dated on a matching weekday, repeating every 2 weeks + forever. Lines with no week type applied to both weeks and repeat weekly. + Anchor dates are Mondays of two consecutive week-type blocks (2024-01-01 + is a Monday), so each (weekday, week_type) pair occurs exactly once. + """ + if not openupgrade.column_exists(cr, "resource_calendar_attendance", "week_type"): + return + openupgrade.logged_query( + cr, + """ + WITH anchors AS ( + SELECT + anchor::date AS anchor_date, + MOD((anchor::date - DATE '0001-01-01') / 7, 2) AS week_type, + (EXTRACT(ISODOW FROM anchor::date)::int - 1)::varchar AS dayofweek + FROM generate_series( + DATE '2024-01-01', + DATE '2024-01-14', + INTERVAL '1 day' + ) AS anchor + ) + UPDATE resource_calendar_attendance AS attendance + SET + date = anchors.anchor_date, + recurrency = TRUE, + recurrency_type = 'weeks', + recurrency_interval = CASE + WHEN attendance.week_type IS NULL THEN 1 + ELSE 2 + END, + recurrency_end_type = 'forever', + recurrency_until = DATE '9999-12-31', + dayofweek = anchors.dayofweek + FROM resource_calendar AS calendar, anchors + WHERE attendance.calendar_id = calendar.id + AND calendar.calendar_type = 'variable' + AND attendance.date IS NULL + AND anchors.dayofweek = attendance.dayofweek + AND ( + (attendance.week_type IS NULL AND anchors.week_type = 0) + OR attendance.week_type = anchors.week_type::varchar + ) + """, + ) + + +def _drop_undefined_attendances(cr): + """Undefined calendars have no slots. v19 ignored attendances while the + calendar was flexible; the v20 cleanup cron would delete them anyway. + """ + openupgrade.logged_query( + cr, + """ + DELETE FROM resource_calendar_attendance AS attendance + USING resource_calendar AS calendar + WHERE attendance.calendar_id = calendar.id + AND calendar.calendar_type = 'undefined' + """, + ) + + +def _warn_calendar_timezones(cr): + """Per-calendar tz is gone. Resource tz is unchanged; company tz is what + calendar-level math uses now. Log calendars that will not match. + """ + if not openupgrade.column_exists(cr, "resource_calendar", "tz"): + return + cr.execute( + """ + SELECT calendar.id, calendar.name, calendar.tz, company.tz + FROM resource_calendar AS calendar + JOIN res_company AS company ON company.id = calendar.company_id + WHERE calendar.tz IS NOT NULL + AND company.tz IS NOT NULL + AND calendar.tz <> company.tz + """ + ) + rows = cr.fetchall() + if not rows: + return + _logger.warning( + "resource: %s calendar(s) had a timezone different from their company. " + "v20 keeps a single timezone on the company (and on each resource). " + "Examples: %s", + len(rows), + rows[:10], + ) + + +@openupgrade.migrate() +def migrate(env, version): + _convert_two_week_attendances(env.cr) + _drop_undefined_attendances(env.cr) + _warn_calendar_timezones(env.cr) + openupgrade.load_data(env, "resource", "20.0.1.1/noupdate_changes.xml") diff --git a/openupgrade_scripts/scripts/resource/20.0.1.1/pre-migration.py b/openupgrade_scripts/scripts/resource/20.0.1.1/pre-migration.py new file mode 100644 index 00000000000..b04d351710f --- /dev/null +++ b/openupgrade_scripts/scripts/resource/20.0.1.1/pre-migration.py @@ -0,0 +1,314 @@ +# Copyright 2026 Odoo Community Association (OCA) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from openupgradelib import openupgrade + + +def _map_timezones(cr): + """Map pytz names that zoneinfo dropped, before copying tz onto the company.""" + for table in ("resource_calendar", "resource_resource"): + if not openupgrade.column_exists(cr, table, "tz"): + continue + openupgrade.logged_query( + cr, + f""" + UPDATE {table} + SET tz = CASE tz + WHEN 'Europe/Kiev' THEN 'Europe/Kyiv' + WHEN 'Portugal' THEN 'Europe/Lisbon' + WHEN 'Poland' THEN 'Europe/Warsaw' + ELSE tz + END + WHERE tz IN ('Europe/Kiev', 'Portugal', 'Poland') + """, + ) + + +def _delete_obsolete_attendances(cr): + """Drop lines that cannot survive the v20 checks. + + Lunch is no longer a day period (breaks are just gaps between slots) and + section rows only existed to label the two weeks. Both have a duration of + 0, which the new ``duration_hours > 0`` constraint rejects. + """ + openupgrade.logged_query( + cr, + """ + DELETE FROM resource_calendar_attendance + WHERE display_type = 'line_section' + OR day_period = 'lunch' + """, + ) + # Pure duration lines store the length in duration_hours and leave the + # clock hours at 0. v20 treats "both hours are 0" as duration-based and + # then recomputes duration_hours only for the other lines, which would + # reset these to 0 and fail the check. Materialise the v19 "centered on + # noon" hours so the schedule is preserved as ordinary slots. + openupgrade.logged_query( + cr, + """ + UPDATE resource_calendar_attendance + SET hour_from = GREATEST(0, 12 - duration_hours / 2.0), + hour_to = LEAST(24, 12 + duration_hours / 2.0) + WHERE COALESCE(hour_from, 0) = 0 + AND COALESCE(hour_to, 0) = 0 + AND duration_hours > 0 + AND duration_hours <= 24 + """, + ) + openupgrade.logged_query( + cr, + """ + UPDATE resource_calendar_attendance + SET duration_hours = hour_to - hour_from + WHERE COALESCE(hour_to, 0) > COALESCE(hour_from, 0) + AND ( + duration_hours IS NULL + OR duration_hours <= 0 + OR duration_hours > 24 + ) + """, + ) + openupgrade.logged_query( + cr, + """ + DELETE FROM resource_calendar_attendance + WHERE duration_hours IS NULL + OR duration_hours <= 0 + OR duration_hours > 24 + """, + ) + + +def _assign_fully_flexible_calendars(cr): + """v19 resources with an empty calendar were fully flexible. + + v20 makes ``calendar_id`` required and would otherwise fill the blanks + with the env company's default (fixed) calendar during schema init. + One shared undefined calendar per company keeps the "work any time" + behaviour without giving every such resource its own copy. + """ + openupgrade.logged_query( + cr, + """ + WITH missing AS ( + SELECT DISTINCT + resource.company_id, + COALESCE( + NULLIF(partner.tz, ''), + 'UTC' + ) AS tz + FROM resource_resource AS resource + LEFT JOIN res_company AS company + ON company.id = resource.company_id + LEFT JOIN res_partner AS partner + ON partner.id = company.partner_id + WHERE resource.calendar_id IS NULL + ), + new_calendars AS ( + INSERT INTO resource_calendar ( + name, active, company_id, schedule_type, flexible_hours, + hours_per_day, hours_per_week, full_time_required_hours, + two_weeks_calendar, duration_based, tz, + create_uid, create_date, write_uid, write_date + ) + SELECT + 'Fully Flexible', TRUE, missing.company_id, 'flexible', TRUE, + 0, 0, 0, + FALSE, FALSE, missing.tz, + 1, (now() AT TIME ZONE 'utc'), 1, (now() AT TIME ZONE 'utc') + FROM missing + RETURNING id, company_id + ) + UPDATE resource_resource AS resource + SET calendar_id = new_calendars.id + FROM new_calendars + WHERE resource.calendar_id IS NULL + AND resource.company_id IS NOT DISTINCT FROM new_calendars.company_id + """, + ) + + +def _prepare_calendar_type(cr): + """Fill ``calendar_type`` and ``days_per_week`` before the ORM init. + + ``calendar_type`` defaults to ``fixed``. If that default were applied to + flexible and two-week calendars, the new stored computes would rebuild + ``hours_per_week`` / ``hours_per_day`` from attendances and drop the + figures the user actually set. Pre-filling both columns makes the init + skip those computes. + """ + openupgrade.logged_query( + cr, + """ + ALTER TABLE resource_calendar + ADD COLUMN IF NOT EXISTS calendar_type varchar + """, + ) + openupgrade.logged_query( + cr, + """ + UPDATE resource_calendar + SET calendar_type = CASE + WHEN schedule_type = 'flexible' OR flexible_hours IS TRUE + THEN 'undefined' + WHEN two_weeks_calendar IS TRUE THEN 'variable' + ELSE 'fixed' + END + WHERE calendar_type IS NULL + """, + ) + openupgrade.logged_query( + cr, + """ + ALTER TABLE resource_calendar + ADD COLUMN IF NOT EXISTS days_per_week numeric + """, + ) + openupgrade.logged_query( + cr, + """ + UPDATE resource_calendar + SET days_per_week = CASE + WHEN COALESCE(hours_per_day, 0) > 0 + THEN hours_per_week / hours_per_day + ELSE 0 + END + WHERE calendar_type = 'undefined' + AND days_per_week IS NULL + """, + ) + # v19 _get_days_per_week() averages the two weeks. Keep that figure so a + # later hours_per_day recompute (hours_per_week / days_per_week) stays put. + openupgrade.logged_query( + cr, + """ + UPDATE resource_calendar AS calendar + SET days_per_week = COALESCE(stats.days, 0) + FROM ( + SELECT + attendance.calendar_id, + ( + COUNT(DISTINCT attendance.dayofweek) + FILTER (WHERE attendance.week_type = '0') + + COUNT(DISTINCT attendance.dayofweek) + FILTER (WHERE attendance.week_type = '1') + ) / 2.0 AS days + FROM resource_calendar_attendance AS attendance + GROUP BY attendance.calendar_id + ) AS stats + WHERE calendar.id = stats.calendar_id + AND calendar.calendar_type = 'variable' + AND calendar.days_per_week IS NULL + """, + ) + openupgrade.logged_query( + cr, + """ + UPDATE resource_calendar AS calendar + SET days_per_week = COALESCE(stats.days, 0) + FROM ( + SELECT + attendance.calendar_id, + COUNT(DISTINCT attendance.dayofweek) AS days + FROM resource_calendar_attendance AS attendance + GROUP BY attendance.calendar_id + ) AS stats + WHERE calendar.id = stats.calendar_id + AND calendar.calendar_type = 'fixed' + AND calendar.days_per_week IS NULL + """, + ) + openupgrade.logged_query( + cr, + """ + UPDATE resource_calendar + SET days_per_week = 0 + WHERE days_per_week IS NULL + """, + ) + + +def _prepare_company_tz(cr): + """v19 company.tz was a non-stored related of partner.tz. + + v20 stores it and computes it from the country when the country has a + single timezone, which would discard a partner timezone such as + America/Los_Angeles. Creating the column here, already filled, stops + that compute. Partner tz wins; the company's default calendar tz is only + a fallback for companies whose partner has none. + """ + openupgrade.logged_query( + cr, + """ + ALTER TABLE res_company + ADD COLUMN IF NOT EXISTS tz varchar + """, + ) + openupgrade.logged_query( + cr, + """ + UPDATE res_company AS company + SET tz = COALESCE( + ( + SELECT NULLIF(partner.tz, '') + FROM res_partner AS partner + WHERE partner.id = company.partner_id + ), + ( + SELECT NULLIF(calendar.tz, '') + FROM resource_calendar AS calendar + WHERE calendar.id = company.resource_calendar_id + ), + 'UTC' + ) + WHERE company.tz IS NULL + """, + ) + + +def _rename_time_type(env): + """``time_type`` (leave/other) becomes ``count_as`` (absence/working_time).""" + if not openupgrade.column_exists(env.cr, "resource_calendar_leaves", "time_type"): + return + openupgrade.copy_columns( + env.cr, + {"resource_calendar_leaves": [("time_type", None, None)]}, + ) + openupgrade.rename_fields( + env, + [ + ( + "resource.calendar.leaves", + "resource_calendar_leaves", + "time_type", + "count_as", + ) + ], + ) + openupgrade.map_values( + env.cr, + openupgrade.get_legacy_name("time_type"), + "count_as", + [("leave", "absence"), ("other", "working_time")], + table="resource_calendar_leaves", + ) + openupgrade.logged_query( + env.cr, + """ + UPDATE resource_calendar_leaves + SET count_as = 'absence' + WHERE count_as IS NULL + OR count_as NOT IN ('absence', 'working_time') + """, + ) + + +@openupgrade.migrate() +def migrate(env, version): + _map_timezones(env.cr) + _delete_obsolete_attendances(env.cr) + _assign_fully_flexible_calendars(env.cr) + _prepare_calendar_type(env.cr) + _prepare_company_tz(env.cr) + _rename_time_type(env) diff --git a/openupgrade_scripts/scripts/resource/20.0.1.1/upgrade_analysis.txt b/openupgrade_scripts/scripts/resource/20.0.1.1/upgrade_analysis.txt new file mode 100644 index 00000000000..1b79bc4ac42 --- /dev/null +++ b/openupgrade_scripts/scripts/resource/20.0.1.1/upgrade_analysis.txt @@ -0,0 +1,67 @@ +---Models in module 'resource'--- +---Fields in module 'resource'--- +resource / res.company / tz (selection) : NEW selection_keys: function, hasdefault: compute +resource / resource.calendar / calendar_type (selection) : NEW required, selection_keys: ['fixed', 'undefined', 'variable'], hasdefault: default +resource / resource.calendar / days_per_week (float) : NEW hasdefault: compute +resource / resource.calendar / duration_based (boolean) : DEL +resource / resource.calendar / flexible_hours (boolean) : DEL +resource / resource.calendar / reference_calendar_id (many2one): NEW relation: resource.calendar, hasdefault: default +resource / resource.calendar / schedule_type (selection) : DEL required, selection_keys: ['flexible', 'fully_fixed'] +resource / resource.calendar / two_weeks_calendar (boolean) : DEL +resource / resource.calendar / tz (selection) : DEL required, selection_keys: function +resource / resource.calendar / work_time_rate (float) : is now stored +resource / resource.calendar.attendance / _order : _order is now 'sequence, date, dayofweek, hour_from' ('sequence, week_type, dayofweek, hour_from') +resource / resource.calendar.attendance / date (date) : NEW +resource / resource.calendar.attendance / day_period (selection) : now a function +resource / resource.calendar.attendance / day_period (selection) : selection_keys removed: [lunch] +resource / resource.calendar.attendance / display_type (selection) : DEL selection_keys: ['line_section'] +resource / resource.calendar.attendance / duration_based (boolean) : is now stored +resource / resource.calendar.attendance / duration_based (boolean) : not related anymore +resource / resource.calendar.attendance / duration_based (boolean) : now a function +resource / resource.calendar.attendance / duration_days (float) : DEL +resource / resource.calendar.attendance / duration_hours (float) : not a function anymore +resource / resource.calendar.attendance / name (char) : DEL required +resource / resource.calendar.attendance / recurrency (boolean) : NEW +resource / resource.calendar.attendance / recurrency_count (integer) : NEW hasdefault: default +resource / resource.calendar.attendance / recurrency_end_type (selection): NEW selection_keys: ['date', 'forever', 'times'], hasdefault: default +resource / resource.calendar.attendance / recurrency_excluded_occurences (json): NEW +resource / resource.calendar.attendance / recurrency_interval (integer) : NEW hasdefault: default +resource / resource.calendar.attendance / recurrency_type (selection) : NEW selection_keys: ['days', 'weeks'], hasdefault: default +resource / resource.calendar.attendance / recurrency_until (date) : NEW hasdefault: compute +resource / resource.calendar.attendance / week_type (selection) : DEL selection_keys: ['0', '1'] +resource / resource.calendar.leaves / count_as (selection) : NEW selection_keys: ['absence', 'working_time'], hasdefault: default +resource / resource.calendar.leaves / time_type (selection) : DEL selection_keys: ['leave', 'other'] +resource / resource.resource / calendar_id (many2one) : now required +resource / resource.resource / color (integer) : previously in module resource_mail +---XML records in module 'resource'--- +NEW ir.access: resource.access_resource_calendar_attendance_system +NEW ir.access: resource.access_resource_calendar_attendance_user +NEW ir.access: resource.access_resource_calendar_system +NEW ir.access: resource.access_resource_calendar_user +NEW ir.access: resource.access_resource_resource_all +NEW ir.access: resource.resource_calendar_leaves_rule_group_admin_modify +NEW ir.access: resource.resource_calendar_leaves_rule_group_user_create +NEW ir.access: resource.resource_calendar_leaves_rule_group_user_modify +NEW ir.access: resource.resource_calendar_leaves_rule_multi_company +NEW ir.access: resource.resource_resource_multi_company +NEW ir.actions.act_window: resource.action_resource_material_from_calendar +NEW ir.cron: resource.ir_cron_calendar_clean_up +DEL ir.model.access: resource.access_resource_calendar_attendance_system +DEL ir.model.access: resource.access_resource_calendar_attendance_user +DEL ir.model.access: resource.access_resource_calendar_leaves +DEL ir.model.access: resource.access_resource_calendar_leaves_user +DEL ir.model.access: resource.access_resource_calendar_system +DEL ir.model.access: resource.access_resource_calendar_user +DEL ir.model.access: resource.access_resource_resource +DEL ir.model.access: resource.access_resource_resource_all +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_count +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_duration_hours +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_interval +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_recurrency_until +DEL ir.rule: resource.resource_calendar_leaves_rule_group_admin_modify (noupdate) +DEL ir.rule: resource.resource_calendar_leaves_rule_group_user_create (noupdate) +DEL ir.rule: resource.resource_calendar_leaves_rule_group_user_modify (noupdate) +DEL ir.rule: resource.resource_calendar_leaves_rule_multi_company (noupdate) +DEL ir.rule: resource.resource_resource_multi_company (noupdate) +NEW ir.ui.view: resource.view_resource_calendar_attendance_calendar +NEW ir.ui.view: resource.view_resource_calendar_multi_create_form diff --git a/openupgrade_scripts/scripts/resource/20.0.1.1/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/resource/20.0.1.1/upgrade_analysis_work.txt new file mode 100644 index 00000000000..0eb261b701e --- /dev/null +++ b/openupgrade_scripts/scripts/resource/20.0.1.1/upgrade_analysis_work.txt @@ -0,0 +1,167 @@ +---Models in module 'resource'--- +# NOTHING TO DO + +---Fields in module 'resource'--- +resource / res.company / tz (selection) : NEW selection_keys: function, hasdefault: compute +resource / resource.calendar / tz (selection) : DEL required, selection_keys: function +# DONE: pre-migration adds res_company.tz and fills it from the partner +# timezone (what v19's non-stored related company.tz returned), else the +# company's default calendar tz, else UTC. The column is filled before the +# ORM init so the new compute (country timezone when the country has only +# one) does not overwrite it. +# resource.calendar.tz and resource.resource.tz are mapped for the pytz names +# base also maps (Europe/Kiev, Portugal, Poland) before that copy. +# NOTHING TO DO for keeping a tz per calendar: v20 dropped it (odoo/odoo@2dff65ab, +# later company.tz lives on resource). Post-migration logs calendars whose tz +# differs from the company. Each resource.resource.tz is unchanged apart from +# the obsolete-name map, and that is the timezone v20 uses for a resource. + +resource / resource.calendar / calendar_type (selection) : NEW required, selection_keys: ['fixed', 'undefined', 'variable'], hasdefault: default +resource / resource.calendar / schedule_type (selection) : DEL required, selection_keys: ['flexible', 'fully_fixed'] +resource / resource.calendar / flexible_hours (boolean) : DEL +resource / resource.calendar / two_weeks_calendar (boolean) : DEL +resource / resource.calendar / duration_based (boolean) : DEL +# DONE: pre-migration sets calendar_type before the ORM would default every +# row to 'fixed' (which would recompute hours from attendances): +# flexible / flexible_hours -> undefined +# two_weeks_calendar -> variable (odoo/odoo@31b77987 replaces the +# two-week mode, introduced as a +# removal in odoo/odoo@2dff65ab) +# otherwise -> fixed +# duration_based on the calendar had no successor flag. Pre-migration turns +# pure duration lines (hours 0/0, duration_hours set) into clock hours centered +# on noon, which is how v19 already stored them after the inverse and how v20 +# places duration-based slots. The weekly pattern is unchanged. + +resource / resource.calendar / days_per_week (float) : NEW hasdefault: compute +# DONE: pre-migration fills it so the ORM does not recompute hours_per_day +# (hours_per_week / days_per_week, and that compute does not skip non-fixed +# calendars). Fixed: distinct weekdays. Variable: v19's two-week average. +# Undefined: hours_per_week / hours_per_day, or 0 when there is no daily figure, +# which keeps a fully flexible calendar fully flexible. + +resource / resource.calendar / reference_calendar_id (many2one): NEW relation: resource.calendar, hasdefault: default +# NOTHING TO DO: optional. Empty means full_time_required_hours stays as stored; +# the field is not new, so the changed compute is not run for every row. +# odoo/odoo@8084b882 explicitly has no default reference calendar. + +resource / resource.calendar / work_time_rate (float) : is now stored +# NOTHING TO DO: v19 did not store it (and scaled it by 100). There is no +# column to convert. The ORM computes the v20 ratio +# hours_per_week / full_time_required_hours on the new column. + +resource / resource.calendar.attendance / _order : _order is now 'sequence, date, dayofweek, hour_from' ('sequence, week_type, dayofweek, hour_from') +# NOTHING TO DO: display order only. + +resource / resource.calendar.attendance / date (date) : NEW +resource / resource.calendar.attendance / recurrency (boolean) : NEW +resource / resource.calendar.attendance / recurrency_count (integer) : NEW hasdefault: default +resource / resource.calendar.attendance / recurrency_end_type (selection): NEW selection_keys: ['date', 'forever', 'times'], hasdefault: default +resource / resource.calendar.attendance / recurrency_excluded_occurences (json): NEW +resource / resource.calendar.attendance / recurrency_interval (integer) : NEW hasdefault: default +resource / resource.calendar.attendance / recurrency_type (selection) : NEW selection_keys: ['days', 'weeks'], hasdefault: default +resource / resource.calendar.attendance / recurrency_until (date) : NEW hasdefault: compute +resource / resource.calendar.attendance / week_type (selection) : DEL selection_keys: ['0', '1'] +# DONE: post-migration, for variable calendars only. Each remaining line is +# dated on a weekday whose v19 week_type matches (week_type = +# floor((ordinal - 1) / 7) % 2) and repeats every 2 weeks until 9999-12-31. +# A line with no week type repeats every week. Fixed calendars keep undated +# weekly lines; their new recurrency columns stay at the ORM defaults +# (recurrency null), which the checks allow. The anchor week is 2024-01-01 +# (a Monday) plus the following 13 days, one row per (weekday, week_type). + +resource / resource.calendar.attendance / day_period (selection) : now a function +resource / resource.calendar.attendance / day_period (selection) : selection_keys removed: [lunch] +resource / resource.calendar.attendance / display_type (selection) : DEL selection_keys: ['line_section'] +resource / resource.calendar.attendance / name (char) : DEL required +resource / resource.calendar.attendance / duration_days (float) : DEL +# DONE for lunch and section rows: pre-migration deletes them. Lunch was a +# zero-duration break (v20 models the break as the gap between slots, +# odoo/odoo@2dff65ab) and would fail CHECK(duration_hours > 0). Section rows +# were two-week labels, not working time. name and duration_days have no +# successor; Odoo 20 leaves the orphan columns in place. day_period on the +# surviving rows stays morning/afternoon/full_day. + +resource / resource.calendar.attendance / duration_based (boolean) : is now stored +resource / resource.calendar.attendance / duration_based (boolean) : not related anymore +resource / resource.calendar.attendance / duration_based (boolean) : now a function +resource / resource.calendar.attendance / duration_hours (float) : not a function anymore +# DONE in pre-migration, together with the calendar duration_based note. +# duration_based was a non-stored related of the calendar. v20 stores +# "both clock hours are 0". Lines in that shape with a positive duration are +# given noon-centered hours first, so the new compute does not classify them +# as duration-based and then reset duration_hours to 0 (the compute only +# assigns duration_hours when duration_based is false, and an unassigned +# computed value is written as 0, which fails the new check). +# The analysis line "not a function anymore" does not match current 20.0, +# where duration_hours is still computed and stored. The pre-migration makes +# either behaviour safe: clock hours and duration_hours agree, and both are +# inside (0, 24]. + +resource / resource.calendar.leaves / count_as (selection) : NEW selection_keys: ['absence', 'working_time'], hasdefault: default +resource / resource.calendar.leaves / time_type (selection) : DEL selection_keys: ['leave', 'other'] +# DONE: pre-migration renames the column and maps leave->absence, other->working_time. +# NULL and anything unexpected become absence (the v20 default). + +resource / resource.resource / calendar_id (many2one) : now required +# DONE: pre-migration, before the ORM fills NULL with the env company's +# default fixed calendar. v19 treated an empty calendar as fully flexible +# (the whole interval is working time). One shared "Fully Flexible" calendar +# per company (schedule_type flexible, 0 hours/day and hours/week, so v20 +# _is_fully_flexible() is true) is assigned to those resources. + +resource / resource.resource / color (integer) : previously in module resource_mail +# NOTHING TO DO: the column already exists when resource_mail was installed +# and is created with its default otherwise. The ORM retargets ir.model.fields +# to the resource module. No values to move. + +---XML records in module 'resource'--- +NEW ir.access: resource.access_resource_calendar_attendance_system +NEW ir.access: resource.access_resource_calendar_attendance_user +NEW ir.access: resource.access_resource_calendar_system +NEW ir.access: resource.access_resource_calendar_user +NEW ir.access: resource.access_resource_resource_all +NEW ir.access: resource.resource_calendar_leaves_rule_group_admin_modify +NEW ir.access: resource.resource_calendar_leaves_rule_group_user_create +NEW ir.access: resource.resource_calendar_leaves_rule_group_user_modify +NEW ir.access: resource.resource_calendar_leaves_rule_multi_company +NEW ir.access: resource.resource_resource_multi_company +DEL ir.model.access: resource.access_resource_calendar_attendance_system +DEL ir.model.access: resource.access_resource_calendar_attendance_user +DEL ir.model.access: resource.access_resource_calendar_leaves +DEL ir.model.access: resource.access_resource_calendar_leaves_user +DEL ir.model.access: resource.access_resource_calendar_system +DEL ir.model.access: resource.access_resource_calendar_user +DEL ir.model.access: resource.access_resource_resource +DEL ir.model.access: resource.access_resource_resource_all +DEL ir.rule: resource.resource_calendar_leaves_rule_group_admin_modify (noupdate) +DEL ir.rule: resource.resource_calendar_leaves_rule_group_user_create (noupdate) +DEL ir.rule: resource.resource_calendar_leaves_rule_group_user_modify (noupdate) +DEL ir.rule: resource.resource_calendar_leaves_rule_multi_company (noupdate) +DEL ir.rule: resource.resource_resource_multi_company (noupdate) +# NOTHING TO DO: ir.model.access and ir.rule merged into ir.access by base +# (OCA/OpenUpgrade#6068). This module's XML recreates the standard rules. + +NEW ir.actions.act_window: resource.action_resource_material_from_calendar +NEW ir.cron: resource.ir_cron_calendar_clean_up +NEW ir.ui.view: resource.view_resource_calendar_attendance_calendar +NEW ir.ui.view: resource.view_resource_calendar_multi_create_form +# NOTHING TO DO: loaded by the module update. The cleanup cron deletes +# attendances that do not match the calendar type; post-migration already +# deletes attendances of undefined calendars and dates the variable ones, +# so the cron has nothing left from this migration. + +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_count +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_duration_hours +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_interval +NEW ir.model.constraint: resource.constraint_resource_calendar_attendance_check_recurrency_until +# DONE: pre-migration removes zero-duration rows (lunch, sections, empties) +# and caps the shape of the others so duration_hours is in (0, 24] before the +# check is added. Recurrency checks pass at init because recurrency is still +# null; post-migration sets interval >= 1 and recurrency_until >= date in the +# same UPDATE. + +# noupdate_changes.xml +# DONE: post-migration renames resource.resource_calendar_std from +# "Standard 40 hours/week" to "40 hours/week". The record is noupdate, so the +# module update would not touch it. diff --git a/openupgrade_scripts/scripts/resource_mail/20.0.1.0/upgrade_analysis.txt b/openupgrade_scripts/scripts/resource_mail/20.0.1.0/upgrade_analysis.txt new file mode 100644 index 00000000000..4df305e1a11 --- /dev/null +++ b/openupgrade_scripts/scripts/resource_mail/20.0.1.0/upgrade_analysis.txt @@ -0,0 +1,4 @@ +---Models in module 'resource_mail'--- +---Fields in module 'resource_mail'--- +resource_mail / resource.resource / color (integer) : module is now 'resource' ('resource_mail') +---XML records in module 'resource_mail'--- diff --git a/openupgrade_scripts/scripts/resource_mail/20.0.1.0/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/resource_mail/20.0.1.0/upgrade_analysis_work.txt new file mode 100644 index 00000000000..d4667eb1a19 --- /dev/null +++ b/openupgrade_scripts/scripts/resource_mail/20.0.1.0/upgrade_analysis_work.txt @@ -0,0 +1,13 @@ +---Models in module 'resource_mail'--- +# NOTHING TO DO + +---Fields in module 'resource_mail'--- +resource_mail / resource.resource / color (integer) : module is now 'resource' ('resource_mail') +# NOTHING TO DO: the column stays on resource_resource. resource now owns the +# field (see resource/20.0.1.1). ir.model.fields.module is updated when the +# resource module is reflected, which happens before resource_mail because +# resource_mail depends on resource. im_status was a non-stored related and +# is gone with no column to migrate. + +---XML records in module 'resource_mail'--- +# NOTHING TO DO