post-migration.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # -*- coding: utf-8 -*-
  2. """
  3. Migration script to integrate helpdesk.template with helpdesk.workflow.template
  4. This script migrates all helpdesk.template records to helpdesk.workflow.template,
  5. moving field_ids from template to workflow_template.
  6. """
  7. import logging
  8. from odoo import api, SUPERUSER_ID
  9. _logger = logging.getLogger(__name__)
  10. def migrate(cr, version):
  11. """Migrate helpdesk.template to helpdesk.workflow.template"""
  12. try:
  13. env = api.Environment(cr, SUPERUSER_ID, {})
  14. _logger.info("=" * 80)
  15. _logger.info("Starting migration: helpdesk.template -> helpdesk.workflow.template")
  16. _logger.info("=" * 80)
  17. # 0. Verify pre-migration completed (workflow_template_id column exists)
  18. cr.execute("""
  19. SELECT column_name
  20. FROM information_schema.columns
  21. WHERE table_name = 'helpdesk_template_field'
  22. AND column_name = 'workflow_template_id'
  23. """)
  24. if not cr.fetchone():
  25. _logger.error("❌ Pre-migration not completed! workflow_template_id column does not exist.")
  26. _logger.error("Module update will fail. Please check pre-migration logs.")
  27. raise Exception("Pre-migration required: workflow_template_id column missing")
  28. _logger.info("✅ Pre-migration verified: workflow_template_id column exists")
  29. # 1. Get all helpdesk.template records
  30. templates = env['helpdesk.template'].search([])
  31. _logger.info(f"Found {len(templates)} template(s) to migrate")
  32. if not templates:
  33. _logger.info("No templates to migrate. Migration complete.")
  34. cr.commit()
  35. return
  36. migrated_count = 0
  37. skipped_count = 0
  38. error_count = 0
  39. # 2. For each template, migrate to workflow template
  40. for template in templates:
  41. try:
  42. _logger.info(f"\nProcessing template: {template.name} (ID: {template.id})")
  43. # First, check which teams use this template
  44. teams_using_template = env['helpdesk.team'].search([
  45. ('template_id', '=', template.id)
  46. ])
  47. _logger.info(f" Found {len(teams_using_template)} team(s) using this template")
  48. # Group teams by whether they have workflow_template_id or not
  49. teams_with_workflow = teams_using_template.filtered('workflow_template_id')
  50. teams_without_workflow = teams_using_template.filtered(lambda t: not t.workflow_template_id)
  51. workflow_template = None
  52. # Strategy 1: If teams have workflow_template_id, migrate fields to their existing workflow
  53. if teams_with_workflow:
  54. # Check if all teams use the same workflow_template_id
  55. workflow_ids = teams_with_workflow.mapped('workflow_template_id.id')
  56. unique_workflows = set(workflow_ids)
  57. if len(unique_workflows) == 1:
  58. # All teams use the same workflow template - migrate fields there
  59. workflow_template = teams_with_workflow[0].workflow_template_id
  60. _logger.info(f" All teams use workflow template '{workflow_template.name}' (ID: {workflow_template.id})")
  61. _logger.info(f" Migrating fields from template to existing workflow template...")
  62. else:
  63. # Teams use different workflow templates - use the most common one
  64. from collections import Counter
  65. workflow_counter = Counter(workflow_ids)
  66. most_common_workflow_id = workflow_counter.most_common(1)[0][0]
  67. workflow_template = env['helpdesk.workflow.template'].browse(most_common_workflow_id)
  68. _logger.info(f" Teams use different workflow templates. Using most common: '{workflow_template.name}' (ID: {workflow_template.id})")
  69. _logger.info(f" Migrating fields from template to this workflow template...")
  70. # Strategy 2: If no teams have workflow_template_id, create/find workflow template
  71. if not workflow_template:
  72. # Check if a workflow template with the same name already exists
  73. workflow_template = env['helpdesk.workflow.template'].search([
  74. ('name', '=', template.name)
  75. ], limit=1)
  76. if workflow_template:
  77. _logger.info(f" Workflow template '{workflow_template.name}' already exists (ID: {workflow_template.id})")
  78. _logger.info(f" Migrating fields from template into existing workflow template...")
  79. else:
  80. # Create new workflow template
  81. workflow_template = env['helpdesk.workflow.template'].create({
  82. 'name': template.name,
  83. 'description': template.description or False,
  84. 'active': template.active,
  85. })
  86. _logger.info(f" Created new workflow template: {workflow_template.name} (ID: {workflow_template.id})")
  87. # 3. Migrate field_ids from template to workflow_template
  88. field_count = 0
  89. for field in template.field_ids:
  90. try:
  91. # Check if field already exists in workflow template
  92. existing_field = env['helpdesk.template.field'].search([
  93. ('workflow_template_id', '=', workflow_template.id),
  94. ('field_id', '=', field.field_id.id)
  95. ], limit=1)
  96. if existing_field:
  97. _logger.warning(f" Field '{field.field_id.name}' already exists in workflow template. Skipping.")
  98. continue
  99. # Update field to point to workflow_template_id instead of template_id
  100. field.write({
  101. 'template_id': False, # Clear legacy reference
  102. 'workflow_template_id': workflow_template.id, # Set new reference
  103. })
  104. field_count += 1
  105. _logger.info(f" Migrated field: {field.field_id.name}")
  106. except Exception as e:
  107. _logger.error(f" Error migrating field {field.id}: {e}", exc_info=True)
  108. error_count += 1
  109. continue
  110. _logger.info(f" Migrated {field_count} field(s) to workflow template")
  111. # 4. Update teams that use this template
  112. team_count = 0
  113. for team in teams_using_template:
  114. try:
  115. # If team doesn't have workflow_template_id, assign it
  116. if not team.workflow_template_id:
  117. team.write({
  118. 'workflow_template_id': workflow_template.id,
  119. 'template_id': False, # Clear legacy reference
  120. })
  121. team_count += 1
  122. _logger.info(f" Updated team: {team.name} (ID: {team.id}) - assigned workflow template")
  123. else:
  124. # Team already has workflow_template_id
  125. # If we migrated fields to a different workflow, update it
  126. if team.workflow_template_id.id != workflow_template.id:
  127. _logger.info(f" Team {team.name} had workflow '{team.workflow_template_id.name}', updating to '{workflow_template.name}'")
  128. team.write({
  129. 'workflow_template_id': workflow_template.id,
  130. 'template_id': False, # Clear legacy reference
  131. })
  132. team_count += 1
  133. else:
  134. # Same workflow template - just clear template_id
  135. team.write({
  136. 'template_id': False, # Clear legacy reference
  137. })
  138. _logger.info(f" Team {team.name} already uses workflow '{workflow_template.name}'. Cleared template_id only.")
  139. except Exception as e:
  140. _logger.error(f" Error updating team {team.id}: {e}", exc_info=True)
  141. error_count += 1
  142. continue
  143. _logger.info(f" Updated {team_count} team(s)")
  144. # 5. Regenerate forms for affected teams (skip in production if too many)
  145. teams_affected = env['helpdesk.team'].search([
  146. ('workflow_template_id', '=', workflow_template.id),
  147. ('use_website_helpdesk_form', '=', True)
  148. ])
  149. # Limit regeneration to avoid timeout in production
  150. max_teams_to_regenerate = 50
  151. if len(teams_affected) > max_teams_to_regenerate:
  152. _logger.warning(f" Too many teams ({len(teams_affected)}) to regenerate. Skipping automatic regeneration.")
  153. _logger.warning(f" Please regenerate forms manually or via cron.")
  154. else:
  155. for team in teams_affected:
  156. if team.website_form_view_id:
  157. try:
  158. team._regenerate_form_from_template()
  159. _logger.info(f" Regenerated form for team: {team.name}")
  160. except Exception as e:
  161. _logger.warning(f" Could not regenerate form for team {team.id}: {e}")
  162. # Don't fail migration if form regeneration fails
  163. continue
  164. migrated_count += 1
  165. _logger.info(f"✅ Successfully migrated template: {template.name}")
  166. except Exception as e:
  167. _logger.error(f"❌ Error migrating template {template.id} ({template.name}): {e}", exc_info=True)
  168. error_count += 1
  169. skipped_count += 1
  170. continue
  171. # 6. Summary
  172. _logger.info("\n" + "=" * 80)
  173. _logger.info("Migration Summary:")
  174. _logger.info(f" Templates processed: {len(templates)}")
  175. _logger.info(f" Successfully migrated: {migrated_count}")
  176. _logger.info(f" Skipped/Errors: {skipped_count}")
  177. _logger.info(f" Total errors: {error_count}")
  178. _logger.info("=" * 80)
  179. # 7. Verify migration
  180. remaining_templates = env['helpdesk.template'].search([])
  181. if remaining_templates:
  182. _logger.warning(f"⚠️ {len(remaining_templates)} template(s) still exist. They may have errors or were skipped.")
  183. else:
  184. _logger.info("✅ All templates migrated successfully")
  185. # Check for orphaned fields (fields with template_id but no workflow_template_id)
  186. orphaned_fields = env['helpdesk.template.field'].search([
  187. ('template_id', '!=', False),
  188. ('workflow_template_id', '=', False)
  189. ])
  190. if orphaned_fields:
  191. _logger.warning(f"⚠️ Found {len(orphaned_fields)} orphaned field(s) (have template_id but no workflow_template_id)")
  192. _logger.warning(f" These fields will need manual migration or will be cleaned up later.")
  193. # Commit only if we have successful migrations or no templates to migrate
  194. if migrated_count > 0 or len(templates) == 0:
  195. cr.commit()
  196. _logger.info("✅ Migration completed successfully")
  197. else:
  198. # If all templates failed, rollback but don't block module update
  199. # (data might be corrupted, but module structure is OK)
  200. _logger.error("⚠️ All templates failed to migrate. Rolling back data changes.")
  201. try:
  202. cr.rollback()
  203. except Exception:
  204. pass
  205. _logger.error("Module update will continue, but data migration needs manual intervention.")
  206. except Exception as e:
  207. _logger.error(f"❌ Critical error in migration: {e}", exc_info=True)
  208. try:
  209. cr.rollback()
  210. except Exception:
  211. pass
  212. # Don't raise - allow module update to complete even if migration fails
  213. # The module structure is OK, only data migration failed
  214. _logger.error("Migration failed but module update will continue. Please review logs and fix manually if needed.")