Переглянути джерело

refactor(helpdesk_extras): update models and views, remove migration scripts

odoo 2 місяців тому
батько
коміт
df4ee3f512

+ 1 - 1
helpdesk_extras/__manifest__.py

@@ -1,6 +1,6 @@
 {
     "name": "Helpdesk Extras",
-    "version": "18.0.1.0.12",
+    "version": "18.0.1.0.11",
     "category": "Services/Helpdesk",
     "summary": "Funcionalidades extras para Helpdesk - Compartir equipos y widget de horas",
     "description": """

+ 0 - 249
helpdesk_extras/migrations/18.0.1.0.12/post-migration.py

@@ -1,249 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Migration script to integrate helpdesk.template with helpdesk.workflow.template
-
-This script migrates all helpdesk.template records to helpdesk.workflow.template,
-moving field_ids from template to workflow_template.
-"""
-import logging
-from odoo import api, SUPERUSER_ID
-
-_logger = logging.getLogger(__name__)
-
-
-def migrate(cr, version):
-    """Migrate helpdesk.template to helpdesk.workflow.template"""
-    try:
-        env = api.Environment(cr, SUPERUSER_ID, {})
-        
-        _logger.info("=" * 80)
-        _logger.info("Starting migration: helpdesk.template -> helpdesk.workflow.template")
-        _logger.info("=" * 80)
-        
-        # 0. Verify pre-migration completed (workflow_template_id column exists)
-        cr.execute("""
-            SELECT column_name 
-            FROM information_schema.columns 
-            WHERE table_name = 'helpdesk_template_field' 
-            AND column_name = 'workflow_template_id'
-        """)
-        if not cr.fetchone():
-            _logger.error("❌ Pre-migration not completed! workflow_template_id column does not exist.")
-            _logger.error("Module update will fail. Please check pre-migration logs.")
-            raise Exception("Pre-migration required: workflow_template_id column missing")
-        
-        _logger.info("✅ Pre-migration verified: workflow_template_id column exists")
-        
-        # 1. Get all helpdesk.template records
-        templates = env['helpdesk.template'].search([])
-        _logger.info(f"Found {len(templates)} template(s) to migrate")
-        
-        if not templates:
-            _logger.info("No templates to migrate. Migration complete.")
-            cr.commit()
-            return
-        
-        migrated_count = 0
-        skipped_count = 0
-        error_count = 0
-        
-        # 2. For each template, migrate to workflow template
-        for template in templates:
-            try:
-                _logger.info(f"\nProcessing template: {template.name} (ID: {template.id})")
-                
-                # First, check which teams use this template
-                teams_using_template = env['helpdesk.team'].search([
-                    ('template_id', '=', template.id)
-                ])
-                
-                _logger.info(f"  Found {len(teams_using_template)} team(s) using this template")
-                
-                # Group teams by whether they have workflow_template_id or not
-                teams_with_workflow = teams_using_template.filtered('workflow_template_id')
-                teams_without_workflow = teams_using_template.filtered(lambda t: not t.workflow_template_id)
-                
-                workflow_template = None
-                
-                # Strategy 1: If teams have workflow_template_id, migrate fields to their existing workflow
-                if teams_with_workflow:
-                    # Check if all teams use the same workflow_template_id
-                    workflow_ids = teams_with_workflow.mapped('workflow_template_id.id')
-                    unique_workflows = set(workflow_ids)
-                    
-                    if len(unique_workflows) == 1:
-                        # All teams use the same workflow template - migrate fields there
-                        workflow_template = teams_with_workflow[0].workflow_template_id
-                        _logger.info(f"  All teams use workflow template '{workflow_template.name}' (ID: {workflow_template.id})")
-                        _logger.info(f"  Migrating fields from template to existing workflow template...")
-                    else:
-                        # Teams use different workflow templates - use the most common one
-                        from collections import Counter
-                        workflow_counter = Counter(workflow_ids)
-                        most_common_workflow_id = workflow_counter.most_common(1)[0][0]
-                        workflow_template = env['helpdesk.workflow.template'].browse(most_common_workflow_id)
-                        _logger.info(f"  Teams use different workflow templates. Using most common: '{workflow_template.name}' (ID: {workflow_template.id})")
-                        _logger.info(f"  Migrating fields from template to this workflow template...")
-                
-                # Strategy 2: If no teams have workflow_template_id, create/find workflow template
-                if not workflow_template:
-                    # Check if a workflow template with the same name already exists
-                    workflow_template = env['helpdesk.workflow.template'].search([
-                        ('name', '=', template.name)
-                    ], limit=1)
-                    
-                    if workflow_template:
-                        _logger.info(f"  Workflow template '{workflow_template.name}' already exists (ID: {workflow_template.id})")
-                        _logger.info(f"  Migrating fields from template into existing workflow template...")
-                    else:
-                        # Create new workflow template
-                        workflow_template = env['helpdesk.workflow.template'].create({
-                            'name': template.name,
-                            'description': template.description or False,
-                            'active': template.active,
-                        })
-                        _logger.info(f"  Created new workflow template: {workflow_template.name} (ID: {workflow_template.id})")
-                
-                # 3. Migrate field_ids from template to workflow_template
-                field_count = 0
-                for field in template.field_ids:
-                    try:
-                        # Check if field already exists in workflow template
-                        existing_field = env['helpdesk.template.field'].search([
-                            ('workflow_template_id', '=', workflow_template.id),
-                            ('field_id', '=', field.field_id.id)
-                        ], limit=1)
-                        
-                        if existing_field:
-                            _logger.warning(f"    Field '{field.field_id.name}' already exists in workflow template. Skipping.")
-                            continue
-                        
-                        # Update field to point to workflow_template_id instead of template_id
-                        field.write({
-                            'template_id': False,  # Clear legacy reference
-                            'workflow_template_id': workflow_template.id,  # Set new reference
-                        })
-                        field_count += 1
-                        _logger.info(f"    Migrated field: {field.field_id.name}")
-                    except Exception as e:
-                        _logger.error(f"    Error migrating field {field.id}: {e}", exc_info=True)
-                        error_count += 1
-                        continue
-                
-                _logger.info(f"  Migrated {field_count} field(s) to workflow template")
-                
-                # 4. Update teams that use this template
-                team_count = 0
-                for team in teams_using_template:
-                    try:
-                        # If team doesn't have workflow_template_id, assign it
-                        if not team.workflow_template_id:
-                            team.write({
-                                'workflow_template_id': workflow_template.id,
-                                'template_id': False,  # Clear legacy reference
-                            })
-                            team_count += 1
-                            _logger.info(f"    Updated team: {team.name} (ID: {team.id}) - assigned workflow template")
-                        else:
-                            # Team already has workflow_template_id
-                            # If we migrated fields to a different workflow, update it
-                            if team.workflow_template_id.id != workflow_template.id:
-                                _logger.info(f"    Team {team.name} had workflow '{team.workflow_template_id.name}', updating to '{workflow_template.name}'")
-                                team.write({
-                                    'workflow_template_id': workflow_template.id,
-                                    'template_id': False,  # Clear legacy reference
-                                })
-                                team_count += 1
-                            else:
-                                # Same workflow template - just clear template_id
-                                team.write({
-                                    'template_id': False,  # Clear legacy reference
-                                })
-                                _logger.info(f"    Team {team.name} already uses workflow '{workflow_template.name}'. Cleared template_id only.")
-                    except Exception as e:
-                        _logger.error(f"    Error updating team {team.id}: {e}", exc_info=True)
-                        error_count += 1
-                        continue
-                
-                _logger.info(f"  Updated {team_count} team(s)")
-                
-                # 5. Regenerate forms for affected teams (skip in production if too many)
-                teams_affected = env['helpdesk.team'].search([
-                    ('workflow_template_id', '=', workflow_template.id),
-                    ('use_website_helpdesk_form', '=', True)
-                ])
-                
-                # Limit regeneration to avoid timeout in production
-                max_teams_to_regenerate = 50
-                if len(teams_affected) > max_teams_to_regenerate:
-                    _logger.warning(f"    Too many teams ({len(teams_affected)}) to regenerate. Skipping automatic regeneration.")
-                    _logger.warning(f"    Please regenerate forms manually or via cron.")
-                else:
-                    for team in teams_affected:
-                        if team.website_form_view_id:
-                            try:
-                                team._regenerate_form_from_template()
-                                _logger.info(f"    Regenerated form for team: {team.name}")
-                            except Exception as e:
-                                _logger.warning(f"    Could not regenerate form for team {team.id}: {e}")
-                                # Don't fail migration if form regeneration fails
-                                continue
-                
-                migrated_count += 1
-                _logger.info(f"✅ Successfully migrated template: {template.name}")
-                
-            except Exception as e:
-                _logger.error(f"❌ Error migrating template {template.id} ({template.name}): {e}", exc_info=True)
-                error_count += 1
-                skipped_count += 1
-                continue
-        
-        # 6. Summary
-        _logger.info("\n" + "=" * 80)
-        _logger.info("Migration Summary:")
-        _logger.info(f"  Templates processed: {len(templates)}")
-        _logger.info(f"  Successfully migrated: {migrated_count}")
-        _logger.info(f"  Skipped/Errors: {skipped_count}")
-        _logger.info(f"  Total errors: {error_count}")
-        _logger.info("=" * 80)
-        
-        # 7. Verify migration
-        remaining_templates = env['helpdesk.template'].search([])
-        if remaining_templates:
-            _logger.warning(f"⚠️  {len(remaining_templates)} template(s) still exist. They may have errors or were skipped.")
-        else:
-            _logger.info("✅ All templates migrated successfully")
-        
-        # Check for orphaned fields (fields with template_id but no workflow_template_id)
-        orphaned_fields = env['helpdesk.template.field'].search([
-            ('template_id', '!=', False),
-            ('workflow_template_id', '=', False)
-        ])
-        if orphaned_fields:
-            _logger.warning(f"⚠️  Found {len(orphaned_fields)} orphaned field(s) (have template_id but no workflow_template_id)")
-            _logger.warning(f"    These fields will need manual migration or will be cleaned up later.")
-        
-        # Commit only if we have successful migrations or no templates to migrate
-        if migrated_count > 0 or len(templates) == 0:
-            cr.commit()
-            _logger.info("✅ Migration completed successfully")
-        else:
-            # If all templates failed, rollback but don't block module update
-            # (data might be corrupted, but module structure is OK)
-            _logger.error("⚠️  All templates failed to migrate. Rolling back data changes.")
-            try:
-                cr.rollback()
-            except Exception:
-                pass
-            _logger.error("Module update will continue, but data migration needs manual intervention.")
-        
-    except Exception as e:
-        _logger.error(f"❌ Critical error in migration: {e}", exc_info=True)
-        try:
-            cr.rollback()
-        except Exception:
-            pass
-        # Don't raise - allow module update to complete even if migration fails
-        # The module structure is OK, only data migration failed
-        _logger.error("Migration failed but module update will continue. Please review logs and fix manually if needed.")
-

+ 0 - 216
helpdesk_extras/migrations/18.0.1.0.12/pre-migration.py

@@ -1,216 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Pre-migration script to prepare database schema for template integration
-
-This script runs BEFORE the module update to:
-1. Make template_id nullable
-2. Add workflow_template_id column
-3. Prepare for constraint changes
-"""
-import logging
-
-_logger = logging.getLogger(__name__)
-
-
-def migrate(cr, version):
-    """Prepare database schema for template integration"""
-    try:
-        _logger.info("=" * 80)
-        _logger.info("Starting pre-migration: Preparing database schema")
-        _logger.info("=" * 80)
-        
-        # 1. Check if workflow_template_id column exists
-        cr.execute("""
-            SELECT column_name 
-            FROM information_schema.columns 
-            WHERE table_name = 'helpdesk_template_field' 
-            AND column_name = 'workflow_template_id'
-        """)
-        workflow_col_exists = cr.fetchone()
-        
-        if not workflow_col_exists:
-            _logger.info("Adding workflow_template_id column to helpdesk_template_field...")
-            cr.execute("""
-                ALTER TABLE helpdesk_template_field 
-                ADD COLUMN workflow_template_id INTEGER
-            """)
-            _logger.info("✅ Column workflow_template_id added")
-        else:
-            _logger.info("Column workflow_template_id already exists")
-        
-        # 2. Add foreign key constraint if it doesn't exist
-        cr.execute("""
-            SELECT constraint_name 
-            FROM information_schema.table_constraints 
-            WHERE table_name = 'helpdesk_template_field' 
-            AND constraint_name = 'helpdesk_template_field_workflow_template_id_fkey'
-        """)
-        fk_exists = cr.fetchone()
-        
-        if not fk_exists:
-            _logger.info("Adding foreign key constraint for workflow_template_id...")
-            cr.execute("""
-                ALTER TABLE helpdesk_template_field 
-                ADD CONSTRAINT helpdesk_template_field_workflow_template_id_fkey 
-                FOREIGN KEY (workflow_template_id) 
-                REFERENCES helpdesk_workflow_template(id) 
-                ON DELETE CASCADE
-            """)
-            _logger.info("✅ Foreign key constraint added")
-        else:
-            _logger.info("Foreign key constraint already exists")
-        
-        # 3. Make template_id nullable (if it's not already)
-        cr.execute("""
-            SELECT is_nullable 
-            FROM information_schema.columns 
-            WHERE table_name = 'helpdesk_template_field' 
-            AND column_name = 'template_id'
-        """)
-        template_nullable = cr.fetchone()
-        
-        if template_nullable and template_nullable[0] == 'NO':
-            _logger.info("Making template_id nullable...")
-            # First, drop the NOT NULL constraint by altering the column
-            cr.execute("""
-                ALTER TABLE helpdesk_template_field 
-                ALTER COLUMN template_id DROP NOT NULL
-            """)
-            _logger.info("✅ template_id is now nullable")
-        else:
-            _logger.info("template_id is already nullable")
-        
-        # 4. Add index on workflow_template_id for performance
-        cr.execute("""
-            SELECT indexname 
-            FROM pg_indexes 
-            WHERE tablename = 'helpdesk_template_field' 
-            AND indexname = 'helpdesk_template_field_workflow_template_id_index'
-        """)
-        index_exists = cr.fetchone()
-        
-        if not index_exists:
-            _logger.info("Adding index on workflow_template_id...")
-            cr.execute("""
-                CREATE INDEX helpdesk_template_field_workflow_template_id_index 
-                ON helpdesk_template_field(workflow_template_id)
-            """)
-            _logger.info("✅ Index added")
-        else:
-            _logger.info("Index already exists")
-        
-        # 5. Drop ALL old UNIQUE constraints on helpdesk_template_field (they don't work well with NULLs)
-        # Odoo names constraints as: {table_name}_{constraint_name}
-        # We need to find and drop any UNIQUE constraints that involve template_id or workflow_template_id
-        cr.execute("""
-            SELECT DISTINCT tc.constraint_name
-            FROM information_schema.table_constraints tc
-            JOIN information_schema.key_column_usage kcu 
-                ON tc.constraint_name = kcu.constraint_name
-                AND tc.table_schema = kcu.table_schema
-            WHERE tc.table_name = 'helpdesk_template_field' 
-            AND tc.constraint_type = 'UNIQUE'
-            AND (kcu.column_name IN ('template_id', 'workflow_template_id', 'field_id'))
-        """)
-        old_constraints = cr.fetchall()
-        
-        if old_constraints:
-            _logger.info(f"Found {len(old_constraints)} UNIQUE constraint(s) to drop")
-            for constraint in old_constraints:
-                constraint_name = constraint[0]
-                _logger.info(f"Dropping old constraint: {constraint_name}...")
-                try:
-                    # Use format string with proper escaping
-                    cr.execute(f'ALTER TABLE helpdesk_template_field DROP CONSTRAINT IF EXISTS "{constraint_name}"')
-                    _logger.info(f"✅ Dropped constraint: {constraint_name}")
-                except Exception as e:
-                    _logger.warning(f"Could not drop constraint {constraint_name}: {e}")
-                    # Try alternative method
-                    try:
-                        cr.execute("""
-                            ALTER TABLE helpdesk_template_field 
-                            DROP CONSTRAINT IF EXISTS %s
-                        """, (constraint_name,))
-                        _logger.info(f"✅ Dropped constraint (method 2): {constraint_name}")
-                    except Exception as e2:
-                        _logger.error(f"Failed to drop constraint {constraint_name} with both methods: {e2}")
-        else:
-            _logger.info("No old UNIQUE constraints found to drop")
-        
-        # 6. Add partial unique indexes (work correctly with NULLs)
-        # Index for template_id + field_id (only when template_id IS NOT NULL)
-        cr.execute("""
-            SELECT indexname 
-            FROM pg_indexes 
-            WHERE tablename = 'helpdesk_template_field' 
-            AND indexname = 'helpdesk_template_field_unique_template_field_idx'
-        """)
-        template_idx_exists = cr.fetchone()
-        
-        if not template_idx_exists:
-            _logger.info("Adding partial unique index for template_id + field_id...")
-            cr.execute("""
-                CREATE UNIQUE INDEX helpdesk_template_field_unique_template_field_idx 
-                ON helpdesk_template_field(template_id, field_id)
-                WHERE template_id IS NOT NULL
-            """)
-            _logger.info("✅ Partial unique index for template_id added")
-        else:
-            _logger.info("Partial unique index for template_id already exists")
-        
-        # Index for workflow_template_id + field_id (only when workflow_template_id IS NOT NULL)
-        cr.execute("""
-            SELECT indexname 
-            FROM pg_indexes 
-            WHERE tablename = 'helpdesk_template_field' 
-            AND indexname = 'helpdesk_template_field_unique_workflow_template_field_idx'
-        """)
-        workflow_idx_exists = cr.fetchone()
-        
-        if not workflow_idx_exists:
-            _logger.info("Adding partial unique index for workflow_template_id + field_id...")
-            cr.execute("""
-                CREATE UNIQUE INDEX helpdesk_template_field_unique_workflow_template_field_idx 
-                ON helpdesk_template_field(workflow_template_id, field_id)
-                WHERE workflow_template_id IS NOT NULL
-            """)
-            _logger.info("✅ Partial unique index for workflow_template_id added")
-        else:
-            _logger.info("Partial unique index for workflow_template_id already exists")
-        
-        # 7. Add CHECK constraint to ensure only one of template_id or workflow_template_id is set
-        cr.execute("""
-            SELECT constraint_name 
-            FROM information_schema.table_constraints 
-            WHERE table_name = 'helpdesk_template_field' 
-            AND constraint_name = 'helpdesk_template_field_check_template_or_workflow'
-        """)
-        check_exists = cr.fetchone()
-        
-        if not check_exists:
-            _logger.info("Adding CHECK constraint for template_or_workflow...")
-            cr.execute("""
-                ALTER TABLE helpdesk_template_field 
-                ADD CONSTRAINT helpdesk_template_field_check_template_or_workflow
-                CHECK ((template_id IS NOT NULL AND workflow_template_id IS NULL) 
-                       OR (template_id IS NULL AND workflow_template_id IS NOT NULL))
-            """)
-            _logger.info("✅ CHECK constraint added")
-        else:
-            _logger.info("CHECK constraint already exists")
-        
-        cr.commit()
-        _logger.info("=" * 80)
-        _logger.info("✅ Pre-migration completed successfully")
-        _logger.info("=" * 80)
-        
-    except Exception as e:
-        _logger.error(f"❌ Error in pre-migration: {e}", exc_info=True)
-        try:
-            cr.rollback()
-        except Exception:
-            pass  # Ignore rollback errors
-        # Raise to prevent module update if pre-migration fails
-        # This ensures database is in consistent state
-        raise
-

+ 1 - 1
helpdesk_extras/models/helpdesk_affected_module.py

@@ -38,7 +38,7 @@ class HelpdeskAffectedModule(models.Model):
     )
 
     _sql_constraints = [
-        ('code_unique', 'UNIQUE(code)', 'The module code must be unique!'),
+        ('code_unique', 'UNIQUE(code)', _('The module code must be unique!')),
     ]
 
     def name_get(self):

+ 12 - 23
helpdesk_extras/models/helpdesk_team.py

@@ -37,8 +37,8 @@ class HelpdeskTeamExtras(models.Model):
         """Override create to regenerate form XML if template is set"""
         teams = super().create(vals_list)
         # After create, if template is set and form view exists, regenerate
-        # This handles the case when team is created with template_id or workflow_template_id already set
-        for team in teams.filtered(lambda t: t.use_website_helpdesk_form and t.website_form_view_id and (t.template_id or (t.workflow_template_id and t.workflow_template_id.field_ids))):
+        # This handles the case when team is created with template_id already set
+        for team in teams.filtered(lambda t: t.use_website_helpdesk_form and t.template_id and t.website_form_view_id):
             team._regenerate_form_from_template()
         return teams
 
@@ -47,7 +47,7 @@ class HelpdeskTeamExtras(models.Model):
         result = super()._ensure_submit_form_view()
         # After view is created, if template is set, regenerate form
         # Note: super() may have created views, so we need to refresh to get updated website_form_view_id
-        for team in self.filtered(lambda t: t.use_website_helpdesk_form and (t.template_id or (t.workflow_template_id and t.workflow_template_id.field_ids))):
+        for team in self.filtered(lambda t: t.use_website_helpdesk_form and t.template_id):
             # Refresh to get updated website_form_view_id after super() created it
             team.invalidate_recordset(['website_form_view_id'])
             if team.website_form_view_id:
@@ -57,7 +57,7 @@ class HelpdeskTeamExtras(models.Model):
     def write(self, vals):
         """Override write to regenerate form XML when template changes"""
         result = super().write(vals)
-        if 'template_id' in vals or 'workflow_template_id' in vals:
+        if 'template_id' in vals:
             # Regenerate form XML when template is assigned/changed
             # After super().write(), refresh teams to get updated values
             teams_to_process = self.browse(self.ids).filtered('use_website_helpdesk_form')
@@ -66,12 +66,11 @@ class HelpdeskTeamExtras(models.Model):
                 # This handles the case when template is assigned but view doesn't exist yet
                 if not team.website_form_view_id:
                     # Call _ensure_submit_form_view which will create the view if needed
-                    # This method handles template regeneration for both template_id and workflow_template_id
+                    # This method already handles template regeneration if template_id is set
                     team._ensure_submit_form_view()
                 else:
                     # View exists, regenerate or restore form based on template
-                    has_template = team.template_id or (team.workflow_template_id and team.workflow_template_id.field_ids)
-                    if has_template:
+                    if team.template_id:
                         team._regenerate_form_from_template()
                     else:
                         # If template is removed, restore default form
@@ -394,22 +393,9 @@ class HelpdeskTeamExtras(models.Model):
         return result
 
     def _regenerate_form_from_template(self):
-        """Regenerate the website form XML based on the template (supports both legacy template_id and workflow_template_id)"""
+        """Regenerate the website form XML based on the template"""
         self.ensure_one()
-        
-        # Get template fields from either legacy template or workflow template
-        template_fields = self.env['helpdesk.template.field']
-        template_source = None
-        
-        # Priority: workflow_template_id over template_id (legacy)
-        if self.workflow_template_id and self.workflow_template_id.field_ids:
-            template_fields = self.workflow_template_id.field_ids.sorted('sequence')
-            template_source = f"workflow_template {self.workflow_template_id.id}"
-        elif self.template_id and self.template_id.field_ids:
-            template_fields = self.template_id.field_ids.sorted('sequence')
-            template_source = f"template {self.template_id.id}"
-        
-        if not template_fields or not self.website_form_view_id:
+        if not self.template_id or not self.website_form_view_id:
             return
 
         # Get base form structure (from default template)
@@ -436,9 +422,12 @@ class HelpdeskTeamExtras(models.Model):
         
         # Create environment with website language for translations
         env_lang = self.env(context=dict(self.env.context, lang=lang))
+
+        # Get template fields sorted by sequence
+        template_fields = self.template_id.field_ids.sorted('sequence')
         
         # Log template fields for debugging
-        _logger.info(f"Regenerating form for team {self.id}, {template_source} with {len(template_fields)} fields")
+        _logger.info(f"Regenerating form for team {self.id}, template {self.template_id.id} with {len(template_fields)} fields")
         for tf in template_fields:
             _logger.info(f"  - Field: {tf.field_id.name if tf.field_id else 'None'} (type: {tf.field_id.ttype if tf.field_id else 'None'})")
         

+ 11 - 87
helpdesk_extras/models/helpdesk_template.py

@@ -231,18 +231,9 @@ class HelpdeskTemplateField(models.Model):
     template_id = fields.Many2one(
         'helpdesk.template',
         string='Template',
-        required=False,  # Made optional to support workflow_template_id
-        ondelete='cascade',
-        index=True,
-        help="Legacy template (deprecated - use workflow_template_id instead)"
-    )
-    workflow_template_id = fields.Many2one(
-        'helpdesk.workflow.template',
-        string='Workflow Template',
-        required=False,  # Made optional to support template_id (legacy)
+        required=True,
         ondelete='cascade',
-        index=True,
-        help="Workflow template containing this field"
+        index=True
     )
     field_id = fields.Many2one(
         'ir.model.fields',
@@ -513,11 +504,8 @@ class HelpdeskTemplateField(models.Model):
             self.visibility_between = False
 
     _sql_constraints = [
-        # Note: UNIQUE constraints with NULLs need special handling
-        # PostgreSQL allows multiple NULLs in UNIQUE constraints, so we use partial indexes via pre-migration
-        # The CHECK constraint ensures only one of template_id or workflow_template_id is set
-        # Note: CHECK constraint added via pre-migration script to avoid issues during module update
-        # The constraint is: (template_id IS NOT NULL AND workflow_template_id IS NULL) OR (template_id IS NULL AND workflow_template_id IS NOT NULL)
+        ('unique_template_field', 'unique(template_id, field_id)',
+         'A field can only be added once to a template')
     ]
 
     @api.model
@@ -626,11 +614,10 @@ class HelpdeskTemplateField(models.Model):
         
         fields_created = super().create(vals_list)
         
-        # Get unique templates that were modified (both legacy and workflow)
+        # Get unique templates that were modified
         templates = fields_created.mapped('template_id')
-        workflow_templates = fields_created.mapped('workflow_template_id')
         
-        # Regenerate forms in all teams using legacy templates
+        # Regenerate forms in all teams using these templates
         for template in templates:
             if not template:
                 continue
@@ -650,26 +637,6 @@ class HelpdeskTemplateField(models.Model):
                     except Exception as e:
                         _logger.error(f"Error regenerating form for team {team.id}: {e}", exc_info=True)
         
-        # Regenerate forms in all teams using workflow templates
-        for workflow_template in workflow_templates:
-            if not workflow_template:
-                continue
-            teams = self.env['helpdesk.team'].search([
-                ('workflow_template_id', '=', workflow_template.id),
-                ('use_website_helpdesk_form', '=', True)
-            ])
-            for team in teams:
-                # Ensure view exists before regenerating
-                if not team.website_form_view_id:
-                    team._ensure_submit_form_view()
-                # Regenerate form if view exists
-                if team.website_form_view_id:
-                    try:
-                        team._regenerate_form_from_template()
-                        _logger.info(f"Regenerated form for team {team.id} after adding field to workflow template {workflow_template.id}")
-                    except Exception as e:
-                        _logger.error(f"Error regenerating form for team {team.id}: {e}", exc_info=True)
-        
         return fields_created
 
     def write(self, vals):
@@ -696,13 +663,11 @@ class HelpdeskTemplateField(models.Model):
         if any(key in vals for key in ['field_id', 'sequence', 'required', 'visibility_dependency', 
                                        'visibility_condition', 'visibility_comparator', 'label_custom', 
                                        'model_required', 'placeholder', 'default_value', 'help_text', 
-                                       'widget', 'selection_options', 'rows', 'input_type', 'selection_type',
-                                       'template_id', 'workflow_template_id']):
-            # Get unique templates that were modified (both legacy and workflow)
+                                       'widget', 'selection_options', 'rows', 'input_type', 'selection_type']):
+            # Get unique templates that were modified
             templates = self.mapped('template_id')
-            workflow_templates = self.mapped('workflow_template_id')
             
-            # Regenerate forms in all teams using legacy templates
+            # Regenerate forms in all teams using these templates
             for template in templates:
                 if not template:
                     continue
@@ -721,26 +686,6 @@ class HelpdeskTemplateField(models.Model):
                             _logger.info(f"Regenerated form for team {team.id} after modifying field in template {template.id}")
                         except Exception as e:
                             _logger.error(f"Error regenerating form for team {team.id}: {e}", exc_info=True)
-            
-            # Regenerate forms in all teams using workflow templates
-            for workflow_template in workflow_templates:
-                if not workflow_template:
-                    continue
-                teams = self.env['helpdesk.team'].search([
-                    ('workflow_template_id', '=', workflow_template.id),
-                    ('use_website_helpdesk_form', '=', True)
-                ])
-                for team in teams:
-                    # Ensure view exists before regenerating
-                    if not team.website_form_view_id:
-                        team._ensure_submit_form_view()
-                    # Regenerate form if view exists
-                    if team.website_form_view_id:
-                        try:
-                            team._regenerate_form_from_template()
-                            _logger.info(f"Regenerated form for team {team.id} after modifying field in workflow template {workflow_template.id}")
-                        except Exception as e:
-                            _logger.error(f"Error regenerating form for team {team.id}: {e}", exc_info=True)
         
         return result
 
@@ -756,13 +701,12 @@ class HelpdeskTemplateField(models.Model):
                 % ', '.join(field_names)
             )
         
-        # Get templates before deletion (both legacy and workflow)
+        # Get templates before deletion
         templates = self.mapped('template_id')
-        workflow_templates = self.mapped('workflow_template_id')
         
         result = super().unlink()
         
-        # Regenerate forms in all teams using legacy templates
+        # Regenerate forms in all teams using these templates
         for template in templates:
             if not template:
                 continue
@@ -782,24 +726,4 @@ class HelpdeskTemplateField(models.Model):
                     except Exception as e:
                         _logger.error(f"Error regenerating form for team {team.id}: {e}", exc_info=True)
         
-        # Regenerate forms in all teams using workflow templates
-        for workflow_template in workflow_templates:
-            if not workflow_template:
-                continue
-            teams = self.env['helpdesk.team'].search([
-                ('workflow_template_id', '=', workflow_template.id),
-                ('use_website_helpdesk_form', '=', True)
-            ])
-            for team in teams:
-                # Ensure view exists before regenerating
-                if not team.website_form_view_id:
-                    team._ensure_submit_form_view()
-                # Regenerate form if view exists
-                if team.website_form_view_id:
-                    try:
-                        team._regenerate_form_from_template()
-                        _logger.info(f"Regenerated form for team {team.id} after removing field from workflow template {workflow_template.id}")
-                    except Exception as e:
-                        _logger.error(f"Error regenerating form for team {team.id}: {e}", exc_info=True)
-        
         return result

+ 6 - 15
helpdesk_extras/models/helpdesk_ticket.py

@@ -87,13 +87,11 @@ class HelpdeskTicket(models.Model):
         help=_("Files attached to this ticket")
     )
 
-    @api.depends('team_id.template_id', 'team_id.workflow_template_id')
+    @api.depends('team_id.template_id')
     def _compute_has_template(self):
-        """Compute if team has a template (supports both legacy template_id and workflow_template_id)"""
+        """Compute if team has a template"""
         for ticket in self:
-            has_legacy_template = bool(ticket.team_id and ticket.team_id.template_id)
-            has_workflow_template = bool(ticket.team_id and ticket.team_id.workflow_template_id and ticket.team_id.workflow_template_id.field_ids)
-            ticket.has_template = has_legacy_template or has_workflow_template
+            ticket.has_template = bool(ticket.team_id and ticket.team_id.template_id)
 
     @api.model
     def _default_request_type_id(self):
@@ -105,18 +103,11 @@ class HelpdeskTicket(models.Model):
         return incident_type.id if incident_type else False
 
     def _get_template_fields(self):
-        """Get template fields for this ticket's team (supports both legacy template_id and workflow_template_id)"""
+        """Get template fields for this ticket's team"""
         self.ensure_one()
-        if not self.team_id:
+        if not self.team_id or not self.team_id.template_id:
             return self.env['helpdesk.template.field']
-        
-        # Priority: workflow_template_id over template_id (legacy)
-        if self.team_id.workflow_template_id and self.team_id.workflow_template_id.field_ids:
-            return self.team_id.workflow_template_id.field_ids.sorted('sequence')
-        elif self.team_id.template_id and self.team_id.template_id.field_ids:
-            return self.team_id.template_id.field_ids.sorted('sequence')
-        
-        return self.env['helpdesk.template.field']
+        return self.team_id.template_id.field_ids.sorted('sequence')
 
     @api.onchange('request_type_id', 'business_impact')
     def _onchange_compute_priority(self):

+ 1 - 14
helpdesk_extras/models/helpdesk_workflow_template.py

@@ -41,13 +41,6 @@ class HelpdeskWorkflowTemplate(models.Model):
         string='SLA Policies',
         help='SLA policies included in this workflow template'
     )
-    field_ids = fields.One2many(
-        'helpdesk.template.field',
-        'workflow_template_id',
-        string='Fields',
-        copy=True,
-        help='Fields included in this workflow template for ticket forms'
-    )
     stage_count = fields.Integer(
         string='Stages Count',
         compute='_compute_counts',
@@ -58,11 +51,6 @@ class HelpdeskWorkflowTemplate(models.Model):
         compute='_compute_counts',
         store=False
     )
-    field_count = fields.Integer(
-        string='Fields Count',
-        compute='_compute_counts',
-        store=False
-    )
     team_ids = fields.One2many(
         'helpdesk.team',
         'workflow_template_id',
@@ -75,13 +63,12 @@ class HelpdeskWorkflowTemplate(models.Model):
         store=False
     )
 
-    @api.depends('stage_template_ids', 'sla_template_ids', 'team_ids', 'field_ids')
+    @api.depends('stage_template_ids', 'sla_template_ids', 'team_ids')
     def _compute_counts(self):
         for template in self:
             template.stage_count = len(template.stage_template_ids)
             template.sla_count = len(template.sla_template_ids)
             template.team_count = len(template.team_ids)
-            template.field_count = len(template.field_ids)
 
     def action_view_teams(self):
         """Open teams using this template"""

+ 4 - 4
helpdesk_extras/views/helpdesk_team_views.xml

@@ -17,7 +17,7 @@
             <xpath expr="//div[@id='channels']" position="before">
                 <h2>Workflow Template</h2>
                 <div class="row mt16 o_settings_container">
-                    <setting string="Workflow Template" help="Select a workflow template to set up fields, stages and SLA policies. This replaces the legacy Template field.">
+                    <setting string="Workflow Template" help="Select a workflow template to quickly set up stages and SLA policies">
                         <field name="workflow_template_id" options="{'no_create': True}"/>
                         <button name="%(helpdesk_extras.helpdesk_workflow_template_apply_wizard_action)d"
                                 type="action"
@@ -26,10 +26,10 @@
                                 context="{'active_id': id, 'default_team_id': id}"/>
                     </setting>
                 </div>
-                <h2>Template (Legacy - Deprecated)</h2>
+                <h2>Template</h2>
                 <div class="row mt16 o_settings_container">
-                    <setting string="Ticket Template (Legacy)" help="⚠️ DEPRECATED: Use Workflow Template instead. This field will be removed in a future version. If you have a template here, it will be migrated automatically.">
-                        <field name="template_id" options="{'no_create': True}" attrs="{'readonly': [('workflow_template_id', '!=', False)]}"/>
+                    <setting string="Ticket Template" help="Template to use for tickets in this team">
+                        <field name="template_id" options="{'no_create': True}"/>
                     </setting>
                 </div>
                 <h2>Collaborators</h2>

+ 2 - 66
helpdesk_extras/views/helpdesk_workflow_template_views.xml

@@ -8,7 +8,6 @@
             <list string="Workflow Templates" decoration-muted="active == False">
                 <field name="sequence" widget="handle" invisible="1"/>
                 <field name="name"/>
-                <field name="field_count" string="Fields" sum="Total Fields"/>
                 <field name="stage_count" string="Stages" sum="Total Stages"/>
                 <field name="sla_count" string="SLA Policies" sum="Total SLAs"/>
                 <field name="team_count" string="Teams Using"/>
@@ -40,15 +39,11 @@
                             </div>
                             <div class="o_kanban_card_content">
                                 <div class="row mb-2">
-                                    <div class="col-4">
-                                        <div class="text-muted small">Fields</div>
-                                        <div class="fw-bold"><field name="field_count"/></div>
-                                    </div>
-                                    <div class="col-4">
+                                    <div class="col-6">
                                         <div class="text-muted small">Stages</div>
                                         <div class="fw-bold"><field name="stage_count"/></div>
                                     </div>
-                                    <div class="col-4">
+                                    <div class="col-6">
                                         <div class="text-muted small">SLA Policies</div>
                                         <div class="fw-bold"><field name="sla_count"/></div>
                                     </div>
@@ -88,64 +83,6 @@
                     </group>
                     
                     <notebook>
-                        <page string="Fields" name="fields">
-                            <field name="field_ids" nolabel="1" widget="helpdesk_template_field_ids">
-                                <list string="Template Fields" editable="bottom">
-                                    <field name="sequence" widget="handle"/>
-                                    <field name="field_id" 
-                                           domain="[('model', '=', 'helpdesk.ticket'), ('website_form_blacklisted', '=', False)]"
-                                           options="{'no_create': True, 'no_open': True}" 
-                                           required="1"/>
-                                    <field name="field_name" readonly="1"/>
-                                    <field name="field_type" readonly="1"/>
-                                    <field name="required"/>
-                                    <field name="model_required" invisible="1"/>
-                                    <field name="label_custom" placeholder="Custom label (optional)"/>
-                                    <field name="placeholder" placeholder="Placeholder text"/>
-                                    <field name="default_value" placeholder="Default value"/>
-                                    <field name="help_text" widget="html" placeholder="Help text (HTML)"/>
-                                    <field name="rows" 
-                                           string="Height (Rows)"
-                                           invisible="field_type not in ['text', 'html']"/>
-                                    <field name="input_type" 
-                                           string="Input Type"
-                                           invisible="field_type != 'char'"/>
-                                    <field name="selection_type" 
-                                           string="Selection Type"
-                                           invisible="field_type not in ['selection', 'many2one']"/>
-                                    <field name="widget" 
-                                           column_invisible="1"
-                                           invisible="field_type not in ['one2many', 'many2many']"/>
-                                    <field name="selection_options" 
-                                           widget="text" 
-                                           column_invisible="1"
-                                           placeholder='[["value1", "Label 1"], ["value2", "Label 2"]]'
-                                           invisible="field_type != 'selection' or field_id.relation"/>
-                                    <field name="visibility_dependency" 
-                                           domain="[('model', '=', 'helpdesk.ticket'), ('website_form_blacklisted', '=', False)]"
-                                           options="{'no_create': True, 'no_open': True}" 
-                                           placeholder="Select field for visibility condition"/>
-                                    <field name="visibility_comparator" 
-                                           placeholder="Select comparator"/>
-                                    <field name="visibility_condition" 
-                                           placeholder="Enter value to compare"
-                                           invisible="visibility_dependency_type in ['many2one', 'selection']"/>
-                                    <field name="visibility_condition_m2o_id" 
-                                           widget="dynamic_many2one"
-                                           placeholder="Select value"
-                                           invisible="visibility_dependency_type != 'many2one'"
-                                           options="{'no_create': True, 'no_open': True}"/>
-                                    <field name="visibility_condition_selection" 
-                                           placeholder="Select value"
-                                           invisible="visibility_dependency_type != 'selection'"/>
-                                    <field name="visibility_between" 
-                                           placeholder="End value for range (date/datetime)"
-                                           invisible="visibility_comparator not in ['between', '!between']"/>
-                                    <field name="visibility_dependency_type" invisible="1"/>
-                                    <field name="visibility_condition_m2o_model" invisible="1"/>
-                                </list>
-                            </field>
-                        </page>
                         <page string="Stages" name="stages">
                             <field name="stage_template_ids" nolabel="1">
                                 <list string="Stages" editable="bottom" default_order="sequence">
@@ -207,7 +144,6 @@
                 <filter string="Active" name="active" domain="[('active', '=', True)]"/>
                 <filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
                 <separator/>
-                <filter string="Has Fields" name="has_fields" domain="[('field_ids', '!=', False)]"/>
                 <filter string="Has Stages" name="has_stages" domain="[('stage_template_ids', '!=', False)]"/>
                 <filter string="Has SLAs" name="has_slas" domain="[('sla_template_ids', '!=', False)]"/>
                 <group expand="0" string="Group By">