Jelajahi Sumber

sync: Update subtrees with latest fixes for Shared Drive operations - google_api: Add Shared Drive parameters to move_folder method - m22tc_google_workspace: Fix variable naming conflicts and improve error handling

root 5 bulan lalu
induk
melakukan
5c210c3158

+ 13 - 11
google_api/models/google_drive_service.py

@@ -489,12 +489,14 @@ class GoogleDriveService(models.AbstractModel):
             }
 
     def move_folder(self, folder_id, new_parent_id):
-        """Move a folder to a new parent"""
+        """Move a folder to a new parent with Shared Drive support"""
         try:
-            # Get current folder info
-            response = self._do_request(f'/drive/v3/files/{folder_id}', {
-                'fields': 'parents'
-            })
+            # Build Shared Drive parameters
+            params = self._build_shared_drive_params()
+            params['fields'] = 'parents'
+            
+            # Get current folder info with Shared Drive support
+            response = self._do_request(f'/drive/v3/files/{folder_id}', params)
             
             current_parents = response.get('parents', [])
             
@@ -504,18 +506,18 @@ class GoogleDriveService(models.AbstractModel):
                     'error': 'Folder has no current parent'
                 }
             
-            # Use PATCH to update the parents
-            update_data = {
+            # Use PATCH to update the parents with Shared Drive support
+            update_params = self._build_shared_drive_params()
+            update_params.update({
                 'addParents': new_parent_id,
                 'removeParents': current_parents[0]
-            }
+            })
             
             # Update the file with new parent
             update_response = self._do_request(
                 f'/drive/v3/files/{folder_id}',
-                update_data,
-                method='PATCH',
-                json_data=update_data
+                update_params,
+                method='PATCH'
             )
             
             return {

+ 154 - 53
m22tc_google_workspace/models/crm_lead.py

@@ -275,16 +275,22 @@ class CrmLead(models.Model):
         
         _logger.info(f"🔍 Checking for existing folder '{folder_name}' in parent {parent_folder_id}")
         
-        folder_id = drive_service.create_or_get_folder(parent_folder_id, folder_name)
-        
-        # Check if it was reused or created
+        # First check if folder already exists
         existing_folders = drive_service.find_folders_by_name(parent_folder_id, f'^{folder_name}$')
         was_reused = len(existing_folders) > 0
         
         if was_reused:
+            folder_id = existing_folders[0]['id']
             _logger.info(f"🔄 REUSING existing folder '{folder_name}' (ID: {folder_id})")
         else:
-            _logger.info(f"📁 CREATED new folder '{folder_name}' (ID: {folder_id})")
+            # Create new folder
+            result = drive_service.create_folder(folder_name, parent_folder_id)
+            if result.get('success'):
+                folder_id = result.get('folder_id')
+                _logger.info(f"📁 CREATED new folder '{folder_name}' (ID: {folder_id})")
+            else:
+                error_msg = result.get('error', 'Unknown error')
+                raise UserError(_('Failed to create Google Drive folder "%s": %s') % (folder_name, error_msg))
         
         return folder_id, was_reused
 
@@ -440,55 +446,100 @@ class CrmLead(models.Model):
             )
 
     def _rename_entire_folder_structure(self, old_components=None, new_components=None):
-        """Unified method to rename folder structure"""
+        """Unified method to rename folder structure with robust error handling"""
         if not self.google_drive_folder_id:
             return
         
-        # Get current structure if needed
-        if old_components is None and new_components is not None:
-            current_structure = self._analyze_crm_folder_structure(self.google_drive_folder_id)
-            if not current_structure:
-                raise UserError(_('Could not analyze current folder structure'))
+        try:
+            drive_service = self.env['google.drive.service']
             
-            old_components = {
-                'primary_name': current_structure.get('primary_folder', {}).get('name', ''),
-                'year': current_structure.get('year_folder', {}).get('name', ''),
-                'opportunity_name': current_structure.get('opportunity_folder', {}).get('name', '')
-            }
-        
-        # Get new components if needed
-        if new_components is None and old_components is not None:
-            new_components = self._get_folder_name_components()
-        
-        drive_service = self.env['google.drive.service']
-        current_folder_id = self.google_drive_folder_id
-        
-        # Navigate up to find primary folder
-        primary_folder_id = self._find_primary_folder_id_crm(current_folder_id)
-        if not primary_folder_id:
-            raise UserError(_('Cannot find primary folder in the structure'))
-        
-        # Rename folders if needed
-        if old_components['primary_name'] != new_components['primary_name']:
-            result = drive_service.rename_folder(primary_folder_id, new_components['primary_name'])
-            if not result.get('success'):
-                raise UserError(_('Failed to rename primary folder: %s') % result.get('error', 'Unknown error'))
-        
-        if old_components['year'] != new_components['year']:
-            year_folder_id = self._find_year_folder_id_crm(primary_folder_id, old_components['year'])
-            if year_folder_id:
-                result = drive_service.rename_folder(year_folder_id, new_components['year'])
-                if not result.get('success'):
-                    raise UserError(_('Failed to rename year folder: %s') % result.get('error', 'Unknown error'))
-        
-        if old_components['opportunity_name'] != new_components['opportunity_name']:
-            result = drive_service.rename_folder(current_folder_id, new_components['opportunity_name'])
-            if not result.get('success'):
-                raise UserError(_('Failed to rename opportunity folder: %s') % result.get('error', 'Unknown error'))
+            _logger.info(f"🔄 Starting folder structure rename for opportunity {self.id}")
+            
+            # First validate that the current folder exists
+            validation = drive_service.validate_folder_id(self.google_drive_folder_id)
+            if not validation.get('valid'):
+                _logger.warning(f"Cannot rename folder {self.google_drive_folder_id}: folder not found or not accessible")
+                raise UserError(_('Cannot rename folder: folder not found or not accessible'))
+            
+            # Get current structure if needed
+            if old_components is None and new_components is not None:
+                current_structure = self._analyze_crm_folder_structure(self.google_drive_folder_id)
+                if not current_structure:
+                    raise UserError(_('Could not analyze current folder structure'))
+                
+                old_components = {
+                    'primary_name': current_structure.get('primary_folder', {}).get('name', ''),
+                    'year': current_structure.get('year_folder', {}).get('name', ''),
+                    'opportunity_name': current_structure.get('opportunity_folder', {}).get('name', '')
+                }
+            
+            # Get new components if needed
+            if new_components is None and old_components is not None:
+                new_components = self._get_folder_name_components()
+            
+            current_folder_id = self.google_drive_folder_id
+            
+            # SOLUCIÓN ROBUSTA: En lugar de buscar el primary folder, vamos a recrear la estructura
+            # Esto evita problemas con folders que ya no existen
+            _logger.info(f"🔄 Using robust approach: recreating folder structure")
+            
+            # Get company root folder
+            company_root_folder_id = self._get_company_root_folder_id()
+            if not company_root_folder_id:
+                raise UserError(_('Company root folder not configured'))
+            
+            # Create new structure with new components
+            _logger.info(f"🔄 Creating new folder structure with components: {new_components}")
+            
+            # Create primary folder
+            new_primary_folder_id, was_reused = self._create_or_get_folder_crm(company_root_folder_id, new_components['primary_name'])
+            _logger.info(f"✅ Primary folder created/found: {new_primary_folder_id}")
+            
+            # Create year folder
+            new_year_folder_id, was_reused = self._create_or_get_folder_crm(new_primary_folder_id, new_components['year'])
+            _logger.info(f"✅ Year folder created/found: {new_year_folder_id}")
+            
+            # Validate current opportunity folder exists before moving
+            _logger.info(f"🔍 Validating current opportunity folder: {current_folder_id}")
+            current_validation = drive_service.validate_folder_id(current_folder_id)
+            if not current_validation.get('valid'):
+                _logger.warning(f"⚠️ Current opportunity folder {current_folder_id} is not accessible: {current_validation.get('error')}")
+                # If folder doesn't exist, we need to create a new one
+                _logger.info(f"🔄 Creating new opportunity folder in the correct structure")
+                
+                # Create new opportunity folder with correct name
+                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)
+                })
+                
+                _logger.info(f"✅ Created new opportunity folder: {new_opportunity_folder_id}")
+                return
+            
+            # Move current opportunity folder to new structure
+            _logger.info(f"🔄 Moving opportunity folder from old structure to new structure")
+            move_result = drive_service.move_folder(current_folder_id, new_year_folder_id)
+            
+            if not move_result.get('success'):
+                _logger.error(f"❌ Failed to move opportunity folder: {move_result.get('error')}")
+                raise UserError(_('Failed to move opportunity folder: %s') % move_result.get('error', 'Unknown error'))
             
+            _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_components['opportunity_name']
+                'google_drive_folder_name': new_structure
             })
+            
+            _logger.info(f"✅ Folder structure rename completed successfully")
+            
+        except Exception as e:
+            _logger.error(f"Error renaming folder structure: {str(e)}")
+            raise
 
     def _move_folder_to_new_company(self, new_company_id):
         """Move only this opportunity's folder to new company"""
@@ -499,15 +550,25 @@ class CrmLead(models.Model):
         new_root_folder_id = new_company.google_drive_crm_folder_id
         
         if not new_root_folder_id:
-                self.message_post(
+            self.message_post(
                 body=_("⚠️ No se puede mover: Nueva empresa no tiene Google Drive configurado."),
-                    message_type='comment'
-                )
-                return
+                message_type='comment'
+            )
+            return
         
         try:
             drive_service = self.env['google.drive.service']
             
+            # First validate that the current folder exists
+            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'
+                )
+                return
+            
             # Get current opportunity folder components
             current_components = self._get_folder_name_components()
             
@@ -533,7 +594,11 @@ class CrmLead(models.Model):
             
         except Exception as e:
             _logger.error(f"Error moviendo folder: {str(e)}")
-            raise
+            # Don't raise the exception, just log it and continue
+            self.message_post(
+                body=_("⚠️ Error moviendo carpeta: %s") % str(e),
+                message_type='comment'
+            )
 
     # ============================================================================
     # MÉTODOS ESPECÍFICOS DE CRM
@@ -543,20 +608,43 @@ class CrmLead(models.Model):
         """Find the primary folder (company/contact level) in the hierarchy"""
         try:
             drive_service = self.env['google.drive.service']
+            
+            _logger.info(f"🔍 Finding primary folder starting from: {start_folder_id}")
+            
+            # First validate the start folder exists
+            validation = drive_service.validate_folder_id(start_folder_id)
+            if not validation.get('valid'):
+                _logger.error(f"❌ Start folder {start_folder_id} is not valid: {validation.get('error')}")
+                return None
+            
             hierarchy = drive_service.navigate_folder_hierarchy(start_folder_id, max_levels=5)
+            _logger.info(f"📁 Hierarchy found: {len(hierarchy)} levels")
             
             for folder_info in hierarchy:
                 folder_name = folder_info.get('name', '')
                 level = folder_info.get('level', 0)
+                folder_id = folder_info.get('id', '')
+                
+                _logger.info(f"📂 Level {level}: {folder_name} (ID: {folder_id})")
                 
                 if level == 0:
                     continue
                 
                 if not folder_name.isdigit() and folder_name not in ['Meets', 'Archivos cliente']:
-                    year_folders = drive_service.find_folders_by_name(folder_info['id'], r'^\d{4}$')
+                    # Validate this folder exists before checking its children
+                    folder_validation = drive_service.validate_folder_id(folder_id)
+                    if not folder_validation.get('valid'):
+                        _logger.warning(f"⚠️ Folder {folder_id} ({folder_name}) is not accessible, skipping")
+                        continue
+                    
+                    year_folders = drive_service.find_folders_by_name(folder_id, r'^\d{4}$')
                     if year_folders:
-                        return folder_info['id']
+                        _logger.info(f"✅ Found primary folder: {folder_name} (ID: {folder_id})")
+                        return folder_id
+                    else:
+                        _logger.info(f"ℹ️ Folder {folder_name} has no year folders, not primary")
             
+            _logger.warning(f"❌ No primary folder found in hierarchy")
             return None
         except Exception as e:
             _logger.error(f"Error finding primary folder (CRM): {str(e)}")
@@ -766,13 +854,26 @@ class CrmLead(models.Model):
             raise UserError(_('No Google Drive folder exists for this opportunity.'))
         
         try:
+            # Add detailed diagnostic information
+            _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()
+            _logger.info(f"📋 Expected components: {expected_components}")
+            
+            # Test folder validation first
+            drive_service = self.env['google.drive.service']
+            validation = drive_service.validate_folder_id(self.google_drive_folder_id)
+            _logger.info(f"✅ Folder validation result: {validation}")
+            
             current_structure = self._analyze_crm_folder_structure(self.google_drive_folder_id)
+            _logger.info(f"📊 Current structure analysis: {current_structure}")
             
             if not current_structure:
                 # Provide a more helpful error message
                 error_msg = f"<strong>❌ Cannot Analyze Folder Structure</strong><br/><br/>"
                 error_msg += f"<strong>Folder ID:</strong> {self.google_drive_folder_id}<br/>"
+                error_msg += f"<strong>Validation Result:</strong> {validation}<br/>"
                 error_msg += f"<strong>Possible Issues:</strong><br/>"
                 error_msg += f"• Folder may not exist or be accessible<br/>"
                 error_msg += f"• Insufficient permissions to access the folder<br/>"