From 3de62019427a5b4d2c8aa628b0d168339202b010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Taymans?= Date: Mon, 5 Jun 2023 16:54:33 +0200 Subject: [PATCH] [OU-ADD] hr Move elements from `hr_contract` to `hr`. Replace M2M relation between hr.plan and hr.plan.activity.type to O2M and warn about data loss. Import noupdate_changes --- docsource/modules150-160.rst | 2 +- .../scripts/hr/16.0.1.1/post-migration.py | 202 ++++++++++++++++++ .../scripts/hr/16.0.1.1/pre-migration.py | 58 +++++ .../scripts/hr/16.0.1.1/tests/__init__.py | 1 + .../scripts/hr/16.0.1.1/tests/data.py | 184 ++++++++++++++++ .../hr/16.0.1.1/tests/test_hr_migration.py | 49 +++++ .../hr/16.0.1.1/upgrade_analysis_work.txt | 69 ++++++ 7 files changed, 564 insertions(+), 1 deletion(-) create mode 100644 openupgrade_scripts/scripts/hr/16.0.1.1/post-migration.py create mode 100644 openupgrade_scripts/scripts/hr/16.0.1.1/pre-migration.py create mode 100644 openupgrade_scripts/scripts/hr/16.0.1.1/tests/__init__.py create mode 100644 openupgrade_scripts/scripts/hr/16.0.1.1/tests/data.py create mode 100644 openupgrade_scripts/scripts/hr/16.0.1.1/tests/test_hr_migration.py create mode 100644 openupgrade_scripts/scripts/hr/16.0.1.1/upgrade_analysis_work.txt diff --git a/docsource/modules150-160.rst b/docsource/modules150-160.rst index b0f5266d47e8..538cb83a9102 100644 --- a/docsource/modules150-160.rst +++ b/docsource/modules150-160.rst @@ -164,7 +164,7 @@ Module coverage 15.0 -> 16.0 +-------------------------------------------------+----------------------+-------------------------------------------------+ | |del| google_spreadsheet | |Obsolete module. | +-------------------------------------------------+----------------------+-------------------------------------------------+ -| hr | | | +| hr | Done | | +-------------------------------------------------+----------------------+-------------------------------------------------+ | hr_attendance | | | +-------------------------------------------------+----------------------+-------------------------------------------------+ diff --git a/openupgrade_scripts/scripts/hr/16.0.1.1/post-migration.py b/openupgrade_scripts/scripts/hr/16.0.1.1/post-migration.py new file mode 100644 index 000000000000..90061d20fc53 --- /dev/null +++ b/openupgrade_scripts/scripts/hr/16.0.1.1/post-migration.py @@ -0,0 +1,202 @@ +# Copyright 2023 Coop IT Easy (https://coopiteasy.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from openupgradelib import openupgrade +from psycopg2.extensions import AsIs + +_logger = logging.getLogger(__name__) + + +def warn_about_dataloss(cr, source_relation_table, relation_comodel_field): + """Warn user about data loss when migrating data from many2many to + many2one. + + :param source_relation_table: The many2many relation table + of the model that will be on the 'one' side of the relation + :param relation_comodel_field: The name of the column containing ids + of the 'many' part of the new relation. + """ + openupgrade.logged_query( + cr, + """ + SELECT DISTINCT %(relation_comodel_field)s + FROM %(source_relation_table)s + WHERE %(relation_comodel_field)s IN ( + SELECT %(relation_comodel_field)s + FROM %(source_relation_table)s + GROUP BY %(relation_comodel_field)s + HAVING COUNT(*) > 1 + ) + """, + { + "source_relation_table": AsIs(source_relation_table), + "relation_comodel_field": AsIs(relation_comodel_field), + }, + ) + for res in cr.fetchall(): + _logger.error( + "hr.plan.activity.type(%s,) is linked to several hr.plan. " + "hr.plan.activity.type can only be linked to one hr.plan. " + "Fix these data before migrating to avoid data loss.", + res[0], + ) + + +def m2m_to_o2m( + env, + model, + field, + source_relation_table, + relation_source_field, + relation_comodel_field, +): + """Transform many2many relations into one2many (with possible data + loss). + + Use rename_tables() in your pre-migrate script to keep the many2many + relation table and give them as 'source_relation_table' argument. + And remove foreign keys constraints with remove_tables_fks(). + + :param model: The target registery model + :param field: The field that changes from m2m to o2m + :param source_relation_table: The (renamed) many2many relation table + :param relation_source_field: The column name of the 'model' id + in the relation table + :param relation_comodel_field: The column name of the comodel id in + the relation table + """ + columns = env[model]._fields.get(field) + target_table = env[columns.comodel_name]._table + target_field = columns.inverse_name + openupgrade.logged_query( + env.cr, + """ + UPDATE %(target_table)s AS target + SET %(target_field)s=source.%(relation_source_field)s + FROM %(source_relation_table)s AS source + WHERE source.%(relation_comodel_field)s=target.id + """, + { + "target_table": AsIs(target_table), + "target_field": AsIs(target_field), + "source_relation_table": AsIs(source_relation_table), + "relation_source_field": AsIs(relation_source_field), + "relation_comodel_field": AsIs(relation_comodel_field), + }, + ) + + +def create_work_contact(env): + """Create work_contact_id for model hr.employee.base + + If the employee is linked to a res.user, we set the partner_id of the + res.user as a work_contact_id. + + If the employee is not linked to a res.user. Then we try to match an + existing partner with the same email address. If one is found, then + we assign it as work_contact_id. If several are found, we raise a + warning and we link the first one found. If none are found, then we + create a new partner. + """ + employees = env["hr.employee"].search([]) + + for employee in employees: + if employee.user_id and employee.user_id.partner_id: + partner = employee.user_id.partner_id + if ( + not employee.work_email + and not employee.mobile_phone + or ( + employee.work_email == partner.email + and employee.mobile_phone == partner.mobile + ) + or (not employee.work_email and employee.mobile_phone == partner.mobile) + or (not employee.mobile_phone and employee.work_email == partner.email) + ): + employee.work_contact_id = partner + _logger.info( + "Set work_contact_id of hr.employee(%s) to " + "res.partner(%s) (the res.user partner).", + employee.id, + partner.id, + ) + else: + matching_partner = env["res.partner"].search( + [ + ("email", "=", employee.work_email), + ("mobile", "=", employee.mobile_phone), + ] + ) + nb_matching_partner = len(matching_partner) + if nb_matching_partner == 1: + employee.work_contact_id = matching_partner + _logger.info( + "Found res.partner(%s) that match hr.employee(%s). " + "work_contact_id is set accordingly.", + matching_partner.id, + employee.id, + ) + elif nb_matching_partner > 1: + partner = matching_partner[0] + employee.work_contact_id = partner + _logger.warning( + "Several res.partner found for hr.employee(%s): " + "res.partner(%s). " + "Arbitrarily, the res.partner(%s) (the first one) " + "is used for work_contact_id of the hr.employee(%s).", + employee.id, + ", ".joins(matching_partner.ids), + partner.id, + employee.id, + ) + else: + partner_vals = { + "name": employee.name, + "email": employee.work_email, + "mobile": employee.mobile_phone, + "company_id": employee.company_id.id, + "image_1920": employee.image_1920, + } + partner = env["res.partner"].create(partner_vals) + employee.work_contact_id = partner + _logger.info( + "No res.partner found for hr.employee(%s). " + "A new partner has been created and linked to " + "the employee: res.partner(%s).", + employee.id, + partner.id, + ) + + +def fill_master_department_id(cr): + """Fill master_department_id based on parent_path""" + openupgrade.logged_query( + cr, + """ + UPDATE hr_department + SET master_department_id = split_part(parent_path, '/', 1)::int + WHERE parent_path is not NULL; + """, + ) + + +@openupgrade.migrate() +def migrate(env, version): + warn_about_dataloss( + env.cr, + "ou_legacy_16_0_hr_plan_hr_plan_activity_type_rel", + "hr_plan_activity_type_id", + ) + m2m_to_o2m( + env, + "hr.plan", + "plan_activity_type_ids", + "ou_legacy_16_0_hr_plan_hr_plan_activity_type_rel", + "hr_plan_id", + "hr_plan_activity_type_id", + ) + fill_master_department_id(env.cr) + create_work_contact(env) + openupgrade.load_data(env.cr, "hr", "16.0.1.1/noupdate_changes.xml") diff --git a/openupgrade_scripts/scripts/hr/16.0.1.1/pre-migration.py b/openupgrade_scripts/scripts/hr/16.0.1.1/pre-migration.py new file mode 100644 index 000000000000..b27d7aca34df --- /dev/null +++ b/openupgrade_scripts/scripts/hr/16.0.1.1/pre-migration.py @@ -0,0 +1,58 @@ +# Copyright 2023 Coop IT Easy (https://coopiteasy.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openupgradelib import openupgrade + +_xmlid_renames = [ + ( + "hr_contract.access_hr_contract_type_manager", + "hr.access_hr_contract_type_manager", + ), +] + +_new_fields = [ + ( + "company_id", # field name + "hr.plan", # module name + False, # SQL table name + "many2one", # field type + False, # SQL field type + "hr", # module name + ), + ( + "company_id", # field name + "hr.plan.activity.type", # module name + False, # SQL table name + "many2one", # field type + False, # SQL field type + "hr", # module name + ), + ( + "master_department_id", # field name + "hr.department", # module name + False, # SQL table name + "many2one", # field type + False, # SQL field type + "hr", # module name + ), +] + + +@openupgrade.migrate() +def migrate(env, version): + openupgrade.rename_xmlids(env.cr, _xmlid_renames) + openupgrade.add_fields(env, _new_fields) + # Backup Many2many relation between hr.plan and hr.plan.activity.type + openupgrade.remove_tables_fks(env.cr, ["hr_plan_hr_plan_activity_type_rel"]) + # get_legacy_name cannot be used here, because there is confilct in + # renaming constrains on this table. Waiting for a fix in + # openupgradelib, we will fix a new table name here. + openupgrade.rename_tables( + env.cr, + [ + ( + "hr_plan_hr_plan_activity_type_rel", + "ou_legacy_16_0_hr_plan_hr_plan_activity_type_rel", + ) + ], + ) diff --git a/openupgrade_scripts/scripts/hr/16.0.1.1/tests/__init__.py b/openupgrade_scripts/scripts/hr/16.0.1.1/tests/__init__.py new file mode 100644 index 000000000000..ac0ab7e61e91 --- /dev/null +++ b/openupgrade_scripts/scripts/hr/16.0.1.1/tests/__init__.py @@ -0,0 +1 @@ +from . import test_hr_migration diff --git a/openupgrade_scripts/scripts/hr/16.0.1.1/tests/data.py b/openupgrade_scripts/scripts/hr/16.0.1.1/tests/data.py new file mode 100644 index 000000000000..9653ec5e5ca7 --- /dev/null +++ b/openupgrade_scripts/scripts/hr/16.0.1.1/tests/data.py @@ -0,0 +1,184 @@ +env = locals().get("env") + +# Image of an employee +# hr/static/img/employee_al-image.jpg +image_base64 = """ +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 +J5gBuNucdWrpnIge6/3HW/tjP1/cTR/T+pH/2Q== +""" + +# Create res.partner +partners = [ + { + "name": "Ponnie Hart", + "email": "ponnie.hart@home.me", + "phone": "+32 2 420 42 42", + "mobile": "+32 420 42 42 42", + }, + { + "name": "Alanis Dubois", + "phone": "+32 2 421 42 42", + "mobile": "+32 421 42 42 42", + "email": "alanis.dubois@example.com", + "image_1920": image_base64, + }, +] +partner_ids = [env["res.partner"].create(partner) for partner in partners] + +# Create res.users +users = [ + { + "login": "ponniehart", + "partner_id": partner_ids[0].id, + }, + { + "login": "alanisdubois", + "partner_id": partner_ids[1].id, + }, +] +user_ids = [env["res.users"].create(user) for user in users] + +# Create hr.employee +employees = [ + { + "name": "Ponnie Hart", + "job_title": "Chief Technical Officer", + "work_phone": "(376)-310-7863", + "mobile_phone": "(538)-672-3185", + "work_email": "ponnie.hart87@example.com", + "image_1920": image_base64, + "user_id": user_ids[0].id, + }, + { + "name": "Alanis Dubois", + "job_title": "Experienced Developer", + "work_phone": "+32 2 421 42 42", + "mobile_phone": "+32 421 42 42 42", + "work_email": "alanis.dubois@example.com", + "image_1920": image_base64, + "user_id": user_ids[1].id, + }, + { + "name": "Anahita Oliver", + "job_title": "Experienced Developer", + "work_phone": "(538)-497-4804", + "mobile_phone": "(538)-672-3185", + "work_email": "anahita.oliver32@example.com", + "image_1920": image_base64, + }, +] + +for employee in employees: + env["hr.employee"].create(employee) + + +# Create nested department +main_department = env["hr.department"].create({"name": "Main department"}) +sub_department = env["hr.department"].create( + {"name": "Sub department", "parent_id": main_department.id} +) +sub_sub_department = env["hr.department"].create( + {"name": "Sub sub department", "parent_id": sub_department.id} +) + + +env.cr.commit() diff --git a/openupgrade_scripts/scripts/hr/16.0.1.1/tests/test_hr_migration.py b/openupgrade_scripts/scripts/hr/16.0.1.1/tests/test_hr_migration.py new file mode 100644 index 000000000000..47060f18fca2 --- /dev/null +++ b/openupgrade_scripts/scripts/hr/16.0.1.1/tests/test_hr_migration.py @@ -0,0 +1,49 @@ +from odoo.tests import TransactionCase + + +class TestHrMigration(TransactionCase): + def test_work_contact_creation(self): + """Make sure work_contact_id has been created""" + anahita_oliver = self.env["hr.employee"].search( + [("name", "=", "Anahita Oliver")] + ) + self.assertEqual( + anahita_oliver.mobile_phone, anahita_oliver.work_contact_id.mobile + ) + self.assertEqual( + anahita_oliver.work_email, anahita_oliver.work_contact_id.email + ) + self.assertEqual( + anahita_oliver.image_1920, anahita_oliver.work_contact_id.image_1920 + ) + + def test_work_contact_link(self): + """Make sure work_contact_id has been linked to an existing + partner + """ + alanis_bubois = self.env["res.partner"].search([("name", "=", "Alanis Dubois")]) + employee = self.env["hr.employee"].search([("name", "=", "Alanis Dubois")]) + self.assertEqual(alanis_bubois.id, employee.work_contact_id.id) + + def test_work_contact_no_link(self): + """Make sure work_contact_id has not been linked to an existing + partner + """ + ponnie_hart = self.env["res.partner"].search([("name", "=", "Ponnie Hart")]) + employee = self.env["hr.employee"].search([("name", "=", "Ponnie Hart")]) + self.assertNotEqual(ponnie_hart.id, employee.work_contact_id.id) + + def test_master_department_id_computation(self): + """Test that master_department_id is correctly filled""" + main_department = self.env["hr.department"].search( + [("name", "=", "Main department")], limit=1 + ) + sub_department = self.env["hr.department"].search( + [("name", "=", "Sub department")], limit=1 + ) + sub_sub_department = self.env["hr.department"].search( + [("name", "=", "Sub sub department")], limit=1 + ) + self.assertEqual(main_department.master_department_id, main_department) + self.assertEqual(sub_department.master_department_id, main_department) + self.assertEqual(sub_sub_department.master_department_id, main_department) diff --git a/openupgrade_scripts/scripts/hr/16.0.1.1/upgrade_analysis_work.txt b/openupgrade_scripts/scripts/hr/16.0.1.1/upgrade_analysis_work.txt new file mode 100644 index 000000000000..c0a19b40d796 --- /dev/null +++ b/openupgrade_scripts/scripts/hr/16.0.1.1/upgrade_analysis_work.txt @@ -0,0 +1,69 @@ +---Models in module 'hr'--- +model hr.contract.type (moved from hr_contract) +# NOTHING TO DO: done by the ORM + +---Fields in module 'hr'--- +hr / hr.contract.type / __last_update (datetime) : previously in module hr_contract +hr / hr.contract.type / _order : previously in module hr_contract +hr / hr.contract.type / display_name (char) : previously in module hr_contract +hr / hr.contract.type / name (char) : previously in module hr_contract +# NOTHING TO DO: done by the ORM + +hr / hr.contract.type / sequence (integer) : NEW +hr / hr.plan / department_id (many2one) : NEW relation: hr.department +hr / res.users / create_employee (boolean) : NEW hasdefault: default +# NOTHING TO DO: new feature + +hr / res.users / create_employee_id (many2one) : NEW relation: hr.employee +# NOTHING TO DO: non stored fields + +hr / resource.resource / employee_id (one2many) : NEW relation: hr.employee +# NOTHING TO DO: inverse field already existing in previous version + +hr / hr.department / parent_path (char) : NEW +hr / hr.department / plan_ids (one2many) : NEW relation: hr.plan +# NOTHING TO DO: Computed by ORM in the module update. + +hr / hr.job / active (boolean) : NEW hasdefault: default +hr / hr.job / contract_type_id (many2one) : NEW relation: hr.contract.type +hr / hr.job / state (selection) : DEL required, selection_keys: ['open', 'recruit'] +# NOTHING TO DO: hr.job does not work with state anymore. + +hr / hr.department / master_department_id (many2one): NEW relation: hr.department, isfunction: function, stored +# DONE: pre/post-migration: create field and compute after parent_path has been filled by ORM. + +hr / hr.plan / company_id (many2one) : NEW relation: res.company, hasdefault: default +hr / hr.plan.activity.type / company_id (many2one) : NEW relation: res.company, hasdefault: default +# DONE: pre-migration: create field to set a default null value in order to not link these elements to a specific company + +hr / hr.employee / mobile_phone (char) : now a function +hr / hr.employee / work_contact_id (many2one) : NEW relation: res.partner +hr / hr.employee / work_email (char) : now a function +# DONE: post-migration: create work_contact_id for each employee + +hr / hr.plan / plan_activity_type_ids (many2many): table is now 'False' ('hr_plan_hr_plan_activity_type_rel') +hr / hr.plan / plan_activity_type_ids (many2many): type is now 'one2many' ('many2many') +# DONE: pre-migration and post-migration: move data from many2many table to plan_id colomn in hr.plan.activity.type + +hr / hr.plan.activity.type / plan_id (many2one) : NEW relation: hr.plan +# DONE: see plan_activity_type_ids from hr.plan + +---XML records in module 'hr'--- +NEW ir.model.access: hr.access_hr_contract_type_manager [renamed from hr_contract module] +# DONE: pre-migration: renamed + +NEW ir.actions.act_window: hr.hr_contract_type_action +NEW ir.actions.server: hr.action_hr_employee_create_user +NEW ir.rule: hr.hr_plan_activity_type_company_rule (noupdate) +NEW ir.rule: hr.hr_plan_company_rule (noupdate) +NEW ir.ui.menu: hr.menu_config_employee +NEW ir.ui.menu: hr.menu_config_recruitment +NEW ir.ui.menu: hr.menu_view_hr_contract_type +DEL ir.ui.menu: hr.menu_config_plan_types +DEL ir.ui.menu: hr.menu_human_resources_configuration_employee +NEW ir.ui.view: hr.hr_contract_type_view_form +NEW ir.ui.view: hr.hr_contract_type_view_tree +NEW ir.ui.view: hr.view_employee_form_smartbutton +NEW ir.ui.view: hr.view_users_simple_form +NEW ir.ui.view: hr.view_users_simple_form_inherit_hr +# NOTHING TO DO: managed by migration mechanism