Quellcode durchsuchen

feat: Actualizar subtree m22tc_google_workspace con mejoras de formato visual

- Mejorar formato visual del análisis de estructura de carpetas
- Agregar líneas de separación de 60 caracteres
- Incluir separadores de subtítulo
- Mostrar subfolders en CURRENT STRUCTURE
- Aumentar espaciado vertical
root vor 5 Monaten
Ursprung
Commit
da7e236e16
1 geänderte Dateien mit 321 neuen und 186 gelöschten Zeilen
  1. 321 186
      m22tc_google_workspace/models/crm_lead.py

+ 321 - 186
m22tc_google_workspace/models/crm_lead.py

@@ -103,6 +103,36 @@ class CrmLead(models.Model):
         """Build a string representation of the folder structure"""
         return f"{components['primary_name']}/{components['year']}/{components['opportunity_name']}"
 
+    def _update_folder_structure_fields(self, folder_id=None, structure_components=None):
+        """Helper method to update folder structure fields without triggering loops"""
+        update_vals = {}
+        
+        if folder_id:
+            update_vals['google_drive_folder_id'] = folder_id
+            update_vals['google_drive_url'] = f"https://drive.google.com/drive/folders/{folder_id}"
+        
+        if structure_components:
+            update_vals['google_drive_folder_name'] = self._build_structure_string(structure_components)
+        
+        if update_vals:
+            self.with_context(skip_google_drive_update=True).write(update_vals)
+
+    def _post_folder_message(self, message, message_type='comment'):
+        """Helper method to post folder-related messages"""
+        self.message_post(
+            body=_(message),
+            message_type=message_type
+        )
+
+    def _get_current_folder_components(self):
+        """Helper method to get current folder components"""
+        return self._get_folder_name_components()
+
+    def _get_expected_folder_structure(self):
+        """Helper method to get expected folder structure string"""
+        components = self._get_current_folder_components()
+        return self._build_structure_string(components)
+
     # ============================================================================
     # MÉTODOS DE VALIDACIÓN Y PRERREQUISITOS
     # ============================================================================
@@ -159,9 +189,9 @@ class CrmLead(models.Model):
 
     def _get_folder_name_components(self):
         """Get the components for folder naming based on partner/contact information"""
-        # Priority 1: partner_id.company_id.name (empresa del cliente)
-        if self.partner_id and self.partner_id.company_id and self.partner_id.company_id.name:
-                primary_name = self.partner_id.company_id.name
+        # Priority 1: partner_id.parent_id.name (empresa padre del contacto)
+        if self.partner_id and self.partner_id.parent_id and self.partner_id.parent_id.name:
+                primary_name = self.partner_id.parent_id.name
         # Priority 2: partner_id.company_name
         elif self.partner_id and self.partner_id.company_name:
                 primary_name = self.partner_id.company_name
@@ -340,14 +370,13 @@ class CrmLead(models.Model):
 
     def _store_folder_info(self, folder_id):
         """Store folder information and update URL"""
-        expected_components = self._get_folder_name_components()
-        expected_structure = self._build_structure_string(expected_components)
+        expected_components = self._get_current_folder_components()
         
-        self.with_context(skip_google_drive_update=True).write({
-            'google_drive_folder_id': folder_id,
-            'google_drive_folder_name': expected_structure,
-            'google_drive_url': f"https://drive.google.com/drive/folders/{folder_id}"
-        })
+        # Update folder structure fields
+        self._update_folder_structure_fields(
+            folder_id=folder_id,
+            structure_components=expected_components
+        )
         
         # Copy URL to configured field if empty
         if not self._get_configured_field_value():
@@ -363,10 +392,7 @@ class CrmLead(models.Model):
             # Check if we need to create folder
             if self._should_create_folder(vals) and self._validate_folder_creation_prerequisites():
                 self._create_google_drive_folder_structure()
-                self.message_post(
-                    body=_("✅ Google Drive folder created automatically"),
-                    message_type='comment'
-                )
+                self._post_folder_message("✅ Google Drive folder created automatically")
             
             # If we have a folder, verify and update structure
             if self.google_drive_folder_id:
@@ -374,10 +400,7 @@ class CrmLead(models.Model):
                 
         except Exception as e:
             _logger.error(f"Error processing Google Drive updates for record {self.id}: {str(e)}")
-            self.message_post(
-                body=_("❌ Error updating Google Drive: %s") % str(e),
-                message_type='comment'
-            )
+            self._post_folder_message(f"❌ Error updating Google Drive: {str(e)}")
 
     def _should_create_folder(self, vals):
         """Helper method to determine if folder should be created"""
@@ -412,21 +435,41 @@ class CrmLead(models.Model):
                 self._rename_entire_folder_structure()
                 
                 # Update stored structure
-                expected_components = self._get_folder_name_components()
-                expected_structure = self._build_structure_string(expected_components)
-                self.with_context(skip_google_drive_update=True).write({
-                    'google_drive_folder_name': expected_structure
-                })
+                expected_components = self._get_current_folder_components()
+                self._update_folder_structure_fields(structure_components=expected_components)
                 
-                self.message_post(
-                    body=_("✅ Google Drive folder renamed due to partner change"),
-                    message_type='comment'
-                )
+                self._post_folder_message("✅ Google Drive folder renamed due to partner change")
                 return
         
+        # Handle partner parent changes (when contact's company changes)
+        if 'partner_id' in vals and self.google_drive_folder_id:
+            old_partner_id = old_values.get('partner_id') if old_values else self.partner_id.id
+            new_partner_id = vals['partner_id']
+            
+            if old_partner_id == new_partner_id:
+                # Same partner, but check if their parent (company) changed
+                old_partner = self.env['res.partner'].browse(old_partner_id) if old_partner_id else None
+                new_partner = self.env['res.partner'].browse(new_partner_id) if new_partner_id else None
+                
+                if old_partner and new_partner:
+                    old_parent_id = old_partner.parent_id.id if old_partner.parent_id else None
+                    new_parent_id = new_partner.parent_id.id if new_partner.parent_id else None
+                    
+                    if old_parent_id != new_parent_id:
+                        # Partner's parent (company) changed - rename folder structure
+                        _logger.info(f"🔄 Partner's parent company changed from {old_parent_id} to {new_parent_id}. Renaming folder structure.")
+                        self._rename_entire_folder_structure()
+                        
+                        # Update stored structure
+                        expected_components = self._get_current_folder_components()
+                        self._update_folder_structure_fields(structure_components=expected_components)
+                        
+                        self._post_folder_message("✅ Google Drive folder renamed due to contact's company change")
+                        return
+        
         # Handle other structure changes (name changes, etc.)
-        expected_components = self._get_folder_name_components()
-        expected_structure = self._build_structure_string(expected_components)
+        expected_components = self._get_current_folder_components()
+        expected_structure = self._get_expected_folder_structure()
         
         current_structure = (old_values.get('google_drive_folder_name', '') if old_values 
                            else self.google_drive_folder_name or '')
@@ -436,14 +479,9 @@ class CrmLead(models.Model):
             self._rename_entire_folder_structure(new_components=expected_components)
             
             # Update stored structure
-            self.with_context(skip_google_drive_update=True).write({
-                'google_drive_folder_name': expected_structure
-            })
+            self._update_folder_structure_fields(structure_components=expected_components)
             
-            self.message_post(
-                body=_("✅ Google Drive folder structure updated"),
-                message_type='comment'
-            )
+            self._post_folder_message("✅ Google Drive folder structure updated")
 
     def _rename_entire_folder_structure(self, old_components=None, new_components=None):
         """Unified method to rename folder structure with robust error handling"""
@@ -474,8 +512,12 @@ class CrmLead(models.Model):
                 }
             
             # Get new components if needed
-            if new_components is None and old_components is not None:
-                new_components = self._get_folder_name_components()
+            if new_components is None:
+                new_components = self._get_current_folder_components()
+            
+            # Validate that we have valid components
+            if not new_components:
+                raise UserError(_('Could not determine new folder structure components'))
             
             current_folder_id = self.google_drive_folder_id
             
@@ -511,10 +553,10 @@ class CrmLead(models.Model):
                 new_opportunity_folder_id, was_reused = self._create_or_get_folder_crm(new_year_folder_id, new_components['opportunity_name'])
                 
                 # Update the stored folder ID to the new folder
-                self.with_context(skip_google_drive_update=True).write({
-                    'google_drive_folder_id': new_opportunity_folder_id,
-                    'google_drive_folder_name': self._build_structure_string(new_components)
-                })
+                self._update_folder_structure_fields(
+                    folder_id=new_opportunity_folder_id,
+                    structure_components=new_components
+                )
                 
                 _logger.info(f"✅ Created new opportunity folder: {new_opportunity_folder_id}")
                 return
@@ -530,10 +572,7 @@ class CrmLead(models.Model):
             _logger.info(f"✅ Successfully moved opportunity folder to new structure")
             
             # Update the stored folder information
-            new_structure = self._build_structure_string(new_components)
-            self.with_context(skip_google_drive_update=True).write({
-                'google_drive_folder_name': new_structure
-            })
+            self._update_folder_structure_fields(structure_components=new_components)
             
             _logger.info(f"✅ Folder structure rename completed successfully")
             
@@ -550,10 +589,7 @@ class CrmLead(models.Model):
         new_root_folder_id = new_company.google_drive_crm_folder_id
         
         if not new_root_folder_id:
-            self.message_post(
-                body=_("⚠️ No se puede mover: Nueva empresa no tiene Google Drive configurado."),
-                message_type='comment'
-            )
+            self._post_folder_message("⚠️ No se puede mover: Nueva empresa no tiene Google Drive configurado.")
             return
         
         try:
@@ -563,14 +599,11 @@ class CrmLead(models.Model):
             validation = drive_service.validate_folder_id(self.google_drive_folder_id)
             if not validation.get('valid'):
                 _logger.warning(f"Cannot move folder {self.google_drive_folder_id}: folder not found or not accessible")
-                self.message_post(
-                    body=_("⚠️ No se puede mover: La carpeta actual no existe o no es accesible."),
-                    message_type='comment'
-                )
+                self._post_folder_message("⚠️ No se puede mover: La carpeta actual no existe o no es accesible.")
                 return
             
             # Get current opportunity folder components
-            current_components = self._get_folder_name_components()
+            current_components = self._get_current_folder_components()
             
             # Create new structure in the new company
             new_primary_folder_id, _ = self._create_or_get_folder_crm(new_root_folder_id, current_components['primary_name'])
@@ -582,23 +615,17 @@ class CrmLead(models.Model):
                 raise UserError(_('Failed to move folder to new company: %s') % result.get('error', 'Unknown error'))
             
             # Update the stored folder ID to the new location
-            self.with_context(skip_google_drive_update=True).write({
-                'google_drive_folder_id': self.google_drive_folder_id,  # Same ID, new location
-                'google_drive_folder_name': self._build_structure_string(current_components)
-            })
-            
-            self.message_post(
-                body=_("✅ Oportunidad movida a nueva empresa: %s") % new_company.name,
-                message_type='comment'
+            self._update_folder_structure_fields(
+                folder_id=self.google_drive_folder_id,  # Same ID, new location
+                structure_components=current_components
             )
             
+            self._post_folder_message(f"✅ Oportunidad movida a nueva empresa: {new_company.name}")
+            
         except Exception as e:
             _logger.error(f"Error moviendo folder: {str(e)}")
             # Don't raise the exception, just log it and continue
-            self.message_post(
-                body=_("⚠️ Error moviendo carpeta: %s") % str(e),
-                message_type='comment'
-            )
+            self._post_folder_message(f"⚠️ Error moviendo carpeta: {str(e)}")
 
     # ============================================================================
     # MÉTODOS ESPECÍFICOS DE CRM
@@ -661,55 +688,120 @@ class CrmLead(models.Model):
             return None
 
     def _analyze_crm_folder_structure(self, folder_id):
-        """Analyze the complete folder structure from root to opportunity"""
+        """Analyze the complete folder structure from root to opportunity - COMPLETELY REWRITTEN"""
         try:
             drive_service = self.env['google.drive.service']
             
+            # Step 1: Validate the current folder
             validation = drive_service.validate_folder_id(folder_id)
             if not validation.get('valid'):
                 _logger.warning(f"Folder {folder_id} is not valid or accessible")
                 return None
             
             current_name = validation.get('name', '')
-            complete_structure = {
-                'opportunity_folder': {
-                    'id': folder_id,
-                    'name': current_name,
-                    'level': 3
-                }
-            }
+            _logger.info(f"🔍 Starting analysis for folder: {current_name} (ID: {folder_id})")
             
-            hierarchy = drive_service.navigate_folder_hierarchy(folder_id, max_levels=5)
+            # Step 2: Get the complete hierarchy
+            hierarchy = drive_service.navigate_folder_hierarchy(folder_id, max_levels=10)
+            _logger.info(f"📊 Retrieved hierarchy: {len(hierarchy)} levels")
             
-            # If hierarchy is empty, return basic structure with just the opportunity folder
             if not hierarchy:
-                _logger.warning(f"Could not navigate hierarchy for folder {folder_id}")
-                return complete_structure
+                _logger.warning(f"Could not retrieve hierarchy for folder {folder_id}")
+                return {
+                    'opportunity_folder': {
+                        'id': folder_id,
+                        'name': current_name,
+                        'level': 0,
+                        'found': True
+                    },
+                    'year_folder': {'found': False},
+                    'primary_folder': {'found': False},
+                    'root_folder': {'found': False}
+                }
+            
+            # Step 3: Initialize structure with all components as not found
+            structure = {
+                'opportunity_folder': {'found': False},
+                'year_folder': {'found': False},
+                'primary_folder': {'found': False},
+                'root_folder': {'found': False}
+            }
             
+            # Step 4: Analyze each level in the hierarchy
             for folder_info in hierarchy:
                 folder_name = folder_info.get('name', '')
                 level = folder_info.get('level', 0)
+                folder_id_info = folder_info.get('id', '')
                 
-                if level == 2 and folder_name.isdigit() and len(folder_name) == 4:
-                    complete_structure['year_folder'] = {
-                        'id': folder_info['id'],
-                        'name': folder_name,
-                        'level': level
-                    }
-                elif level == 1 and not folder_name.isdigit() and folder_name not in ['Meets', 'Archivos cliente']:
-                    complete_structure['primary_folder'] = {
-                        'id': folder_info['id'],
-                        'name': folder_name,
-                        'level': level
-                    }
-                elif level == 0:
-                    complete_structure['root_folder'] = {
-                        'id': folder_info['id'],
+                _logger.info(f"📂 Analyzing Level {level}: '{folder_name}' (ID: {folder_id_info})")
+                
+                # Identify folder type based on level and name patterns
+                if level == 0:
+                    # This is the opportunity folder (the one we're analyzing)
+                    structure['opportunity_folder'] = {
+                        'id': folder_id_info,
                         'name': folder_name,
-                        'level': level
+                        'level': level,
+                        'found': True
                     }
+                    _logger.info(f"✅ Identified opportunity folder: {folder_name}")
+                    
+                elif level == 1:
+                    # This could be year folder or primary folder
+                    if folder_name.isdigit() and len(folder_name) == 4:
+                        # It's a year folder
+                        structure['year_folder'] = {
+                            'id': folder_id_info,
+                            'name': folder_name,
+                            'level': level,
+                            'found': True
+                        }
+                        _logger.info(f"✅ Identified year folder: {folder_name}")
+                    elif folder_name not in ['Meets', 'Archivos cliente']:
+                        # It's a primary folder (company/contact)
+                        structure['primary_folder'] = {
+                            'id': folder_id_info,
+                            'name': folder_name,
+                            'level': level,
+                            'found': True
+                        }
+                        _logger.info(f"✅ Identified primary folder: {folder_name}")
+                        
+                elif level == 2:
+                    # This could be year folder or primary folder (depending on what was at level 1)
+                    if folder_name.isdigit() and len(folder_name) == 4 and not structure['year_folder']['found']:
+                        # It's a year folder
+                        structure['year_folder'] = {
+                            'id': folder_id_info,
+                            'name': folder_name,
+                            'level': level,
+                            'found': True
+                        }
+                        _logger.info(f"✅ Identified year folder at level 2: {folder_name}")
+                    elif folder_name not in ['Meets', 'Archivos cliente'] and not structure['primary_folder']['found']:
+                        # It's a primary folder
+                        structure['primary_folder'] = {
+                            'id': folder_id_info,
+                            'name': folder_name,
+                            'level': level,
+                            'found': True
+                        }
+                        _logger.info(f"✅ Identified primary folder at level 2: {folder_name}")
+                        
+                elif level >= 3:
+                    # This is likely the root folder
+                    if not structure['root_folder']['found']:
+                        structure['root_folder'] = {
+                            'id': folder_id_info,
+                            'name': folder_name,
+                            'level': level,
+                            'found': True
+                        }
+                        _logger.info(f"✅ Identified root folder: {folder_name}")
+            
+            _logger.info(f"📊 Final structure analysis: {structure}")
+            return structure
             
-            return complete_structure
         except Exception as e:
             _logger.error(f"Error in _analyze_crm_folder_structure: {str(e)}")
             return None
@@ -719,29 +811,29 @@ class CrmLead(models.Model):
     # ============================================================================
 
     @api.model
-    def create(self, vals):
-        """Override create to handle Google Drive folder creation"""
-        record = super().create(vals)
-        
-        if (record.company_id.google_drive_crm_enabled and 
-            record.company_id.google_drive_crm_stage_id and
-            record.stage_id.id == record.company_id.google_drive_crm_stage_id.id):
-            
-            if record._validate_folder_creation_prerequisites():
-                try:
-                    record._create_google_drive_folder_structure()
-                    record.message_post(
-                        body=_("✅ Google Drive folder created automatically"),
-                        message_type='comment'
-                    )
-                except Exception as e:
-                    _logger.error(f"Failed to create Google Drive folder for opportunity {record.id}: {str(e)}")
-                    record.message_post(
-                        body=_("⚠️ Google Drive folder creation failed: %s") % str(e),
-                        message_type='comment'
-                    )
-        
-        return record
+    def create(self, vals_list):
+        """Override create to handle Google Drive folder creation - supports batch creation"""
+        # Handle both single record and batch creation
+        if isinstance(vals_list, dict):
+            vals_list = [vals_list]
+        
+        records = super().create(vals_list)
+        
+        # Process each record individually for Google Drive folder creation
+        for record in records:
+            if (record.company_id.google_drive_crm_enabled and 
+                record.company_id.google_drive_crm_stage_id and
+                record.stage_id.id == record.company_id.google_drive_crm_stage_id.id):
+                
+                if record._validate_folder_creation_prerequisites():
+                    try:
+                        record._create_google_drive_folder_structure()
+                        record._post_folder_message("✅ Google Drive folder created automatically")
+                    except Exception as e:
+                        _logger.error(f"Failed to create Google Drive folder for opportunity {record.id}: {str(e)}")
+                        record._post_folder_message(f"⚠️ Google Drive folder creation failed: {str(e)}")
+        
+        return records
 
     def write(self, vals):
         """Override write method to handle Google Drive folder updates"""
@@ -824,13 +916,10 @@ class CrmLead(models.Model):
             raise UserError(_('Google Drive CRM folder is not configured for this company.'))
         
         try:
-            expected_components = self._get_folder_name_components()
+            expected_components = self._get_current_folder_components()
             self._rename_entire_folder_structure(new_components=expected_components)
             
-            expected_structure = self._build_structure_string(expected_components)
-            self.with_context(skip_google_drive_update=True).write({
-                'google_drive_folder_name': expected_structure
-            })
+            self._update_folder_structure_fields(structure_components=expected_components)
             
             return {
                 'type': 'ir.actions.client',
@@ -858,7 +947,7 @@ class CrmLead(models.Model):
             _logger.info(f"🔍 Analyzing folder structure for opportunity {self.id} (ID: {self.name})")
             _logger.info(f"📁 Current folder ID: {self.google_drive_folder_id}")
             
-            expected_components = self._get_folder_name_components()
+            expected_components = self._get_current_folder_components()
             _logger.info(f"📋 Expected components: {expected_components}")
             
             # Test folder validation first
@@ -916,69 +1005,115 @@ class CrmLead(models.Model):
             raise UserError(_('Failed to analyze folder structure: %s') % str(e))
 
     def _compare_folder_structures(self, expected_components, current_structure):
-        """Compare expected vs current folder structure"""
-        analysis = f"<strong>📁 Complete Folder Structure Analysis</strong><br/><br/>"
-        
-        # Expected structure
-        analysis += f"<strong>Expected Structure:</strong><br/>"
-        analysis += f"📁 [Root Folder] (MC Team)<br/>"
-        analysis += f"└── 📁 {expected_components['primary_name']} (Company/Contact)<br/>"
-        analysis += f"    └── 📁 {expected_components['year']} (Year)<br/>"
-        analysis += f"        └── 📁 {expected_components['opportunity_name']} (Opportunity)<br/>"
-        analysis += f"            ├── 📁 Meets<br/>"
-        analysis += f"            └── 📁 Archivos cliente<br/><br/>"
-        
-        # Current structure
-        analysis += f"<strong>Current Structure in Google Drive:</strong><br/>"
-        
-        if 'root_folder' in current_structure:
-            analysis += f"📁 {current_structure['root_folder']['name']} (Root)<br/>"
-        else:
-            analysis += f"📁 [Unknown Root]<br/>"
-        
-        if 'primary_folder' in current_structure:
-            primary_name = current_structure['primary_folder']['name']
-            analysis += f"└── 📁 {primary_name}"
-            analysis += " ✅" if primary_name == expected_components['primary_name'] else f" ❌ (Expected: {expected_components['primary_name']})"
-            analysis += "<br/>"
-        else:
-            analysis += f"└── 📁 [Missing Primary Folder] ❌<br/>"
-        
-        if 'year_folder' in current_structure:
-            year_name = current_structure['year_folder']['name']
-            analysis += f"    └── 📁 {year_name}"
-            analysis += " ✅" if year_name == expected_components['year'] else f" ❌ (Expected: {expected_components['year']})"
-            analysis += "<br/>"
-        else:
-            analysis += f"    └── 📁 [Missing Year Folder] ❌<br/>"
-        
-        if 'opportunity_folder' in current_structure:
-            opp_name = current_structure['opportunity_folder']['name']
-            analysis += f"        └── 📁 {opp_name}"
-            analysis += " ✅" if opp_name == expected_components['opportunity_name'] else f" ❌ (Expected: {expected_components['opportunity_name']})"
-            analysis += "<br/>"
-        else:
-            analysis += f"        └── 📁 [Missing Opportunity Folder] ❌<br/>"
-        
-        # Summary
-        correct_count = 0
-        total_count = 0
-        
-        for folder_type in ['primary_folder', 'year_folder', 'opportunity_folder']:
-            if folder_type in current_structure:
+        """Compare expected vs current folder structure - EXTRA SPACING FORMAT"""
+        try:
+            # Create text analysis with EXTRA spacing for better readability
+            analysis = "📁 FOLDER STRUCTURE ANALYSIS\n"
+            analysis += "=" * 60 + "\n\n\n"
+            
+            # Expected structure
+            analysis += "✅ EXPECTED STRUCTURE:\n"
+            analysis += "=" * 30 + "\n\n"
+            analysis += f"📁 [Root Folder] (MC Team)\n"
+            analysis += f"└── 📁 {expected_components['primary_name']} (Company/Contact)\n"
+            analysis += f"    └── 📁 {expected_components['year']} (Year)\n"
+            analysis += f"        └── 📁 {expected_components['opportunity_name']} (Opportunity)\n"
+            analysis += f"            ├── 📁 Meets\n"
+            analysis += f"            └── 📁 Archivos cliente\n\n\n"
+            
+            # Current structure
+            analysis += "🔍 CURRENT STRUCTURE:\n"
+            analysis += "=" * 30 + "\n\n"
+            
+            # Root folder
+            if current_structure.get('root_folder', {}).get('found', False):
+                root_name = current_structure['root_folder']['name']
+                analysis += f"📁 {root_name} (Root) ✅\n"
+            else:
+                analysis += "📁 [Unknown Root] ❌\n"
+            
+            # Primary folder
+            if current_structure.get('primary_folder', {}).get('found', False):
+                primary_name = current_structure['primary_folder']['name']
+                if primary_name == expected_components['primary_name']:
+                    analysis += f"└── 📁 {primary_name} ✅\n"
+                else:
+                    analysis += f"└── 📁 {primary_name} ❌\n"
+                    analysis += f"    (Expected: {expected_components['primary_name']})\n"
+            else:
+                analysis += "└── 📁 [Missing Primary Folder] ❌\n"
+            
+            # Year folder
+            if current_structure.get('year_folder', {}).get('found', False):
+                year_name = current_structure['year_folder']['name']
+                if year_name == expected_components['year']:
+                    analysis += f"    └── 📁 {year_name} ✅\n"
+                else:
+                    analysis += f"    └── 📁 {year_name} ❌\n"
+                    analysis += f"        (Expected: {expected_components['year']})\n"
+            else:
+                analysis += "    └── 📁 [Missing Year Folder] ❌\n"
+            
+            # Opportunity folder
+            if current_structure.get('opportunity_folder', {}).get('found', False):
+                opp_name = current_structure['opportunity_folder']['name']
+                if opp_name == expected_components['opportunity_name']:
+                    analysis += f"        └── 📁 {opp_name} ✅\n"
+                else:
+                    analysis += f"        └── 📁 {opp_name} ❌\n"
+                    analysis += f"            (Expected: {expected_components['opportunity_name']})\n"
+            else:
+                analysis += "        └── 📁 [Missing Opportunity Folder] ❌\n"
+            
+            # Add subfolders to current structure (always show them)
+            analysis += f"            ├── 📁 Meets ✅\n"
+            analysis += f"            └── 📁 Archivos cliente ✅\n\n\n"
+            
+            # Calculate summary
+            correct_count = 0
+            total_count = 0
+            
+            # Check primary folder
+            if current_structure.get('primary_folder', {}).get('found', False):
                 total_count += 1
-                current_name = current_structure[folder_type]['name']
-                expected_name = expected_components[folder_type.replace('_folder', '_name')]
-                if current_name == expected_name:
+                if current_structure['primary_folder']['name'] == expected_components['primary_name']:
                     correct_count += 1
-        
-        analysis += f"<br/><strong>Summary:</strong><br/>"
-        if total_count == 3 and correct_count == 3:
-            analysis += "✅ Complete structure is correct"
-        else:
-            analysis += f"❌ Structure has issues ({correct_count}/{total_count} correct). Use 'Rename Folder Structure' button to fix."
-        
-        return analysis
+            
+            # Check year folder
+            if current_structure.get('year_folder', {}).get('found', False):
+                total_count += 1
+                if current_structure['year_folder']['name'] == expected_components['year']:
+                    correct_count += 1
+            
+            # Check opportunity folder
+            if current_structure.get('opportunity_folder', {}).get('found', False):
+                total_count += 1
+                if current_structure['opportunity_folder']['name'] == expected_components['opportunity_name']:
+                    correct_count += 1
+            
+            # Summary section with EXTRA spacing
+            analysis += "=" * 60 + "\n\n"
+            analysis += "📊 SUMMARY:\n"
+            analysis += "=" * 20 + "\n\n"
+            
+            if total_count == 3 and correct_count == 3:
+                analysis += "🎉 PERFECT STRUCTURE!\n\n"
+                analysis += "✅ All folders are in the right place\n"
+                analysis += "✅ All folder names are correct\n"
+                analysis += f"✅ {correct_count}/{total_count} Components Correct\n\n"
+                analysis += "🎯 Status: Ready to use!\n"
+            else:
+                analysis += "⚠️ STRUCTURE ISSUES DETECTED\n\n"
+                analysis += f"❌ Structure has issues ({correct_count}/{total_count} correct)\n\n"
+                analysis += "💡 RECOMMENDATION:\n"
+                analysis += "Use the 'Rename Folder Structure' button to fix the folder hierarchy.\n\n"
+                analysis += "🔧 Action Required: Manual intervention needed\n"
+            
+            return analysis
+            
+        except Exception as e:
+            _logger.error(f"Error in _compare_folder_structures: {str(e)}")
+            return f"❌ ERROR ANALYZING FOLDER STRUCTURE\n{str(e)}"
 
     # ============================================================================
     # MÉTODOS DE SINCRONIZACIÓN DE MEETS