|
@@ -103,6 +103,36 @@ class CrmLead(models.Model):
|
|
|
"""Build a string representation of the folder structure"""
|
|
"""Build a string representation of the folder structure"""
|
|
|
return f"{components['primary_name']}/{components['year']}/{components['opportunity_name']}"
|
|
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
|
|
# MÉTODOS DE VALIDACIÓN Y PRERREQUISITOS
|
|
|
# ============================================================================
|
|
# ============================================================================
|
|
@@ -159,9 +189,9 @@ class CrmLead(models.Model):
|
|
|
|
|
|
|
|
def _get_folder_name_components(self):
|
|
def _get_folder_name_components(self):
|
|
|
"""Get the components for folder naming based on partner/contact information"""
|
|
"""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
|
|
# Priority 2: partner_id.company_name
|
|
|
elif self.partner_id and self.partner_id.company_name:
|
|
elif self.partner_id and self.partner_id.company_name:
|
|
|
primary_name = self.partner_id.company_name
|
|
primary_name = self.partner_id.company_name
|
|
@@ -275,16 +305,22 @@ class CrmLead(models.Model):
|
|
|
|
|
|
|
|
_logger.info(f"🔍 Checking for existing folder '{folder_name}' in parent {parent_folder_id}")
|
|
_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}$')
|
|
existing_folders = drive_service.find_folders_by_name(parent_folder_id, f'^{folder_name}$')
|
|
|
was_reused = len(existing_folders) > 0
|
|
was_reused = len(existing_folders) > 0
|
|
|
|
|
|
|
|
if was_reused:
|
|
if was_reused:
|
|
|
|
|
+ folder_id = existing_folders[0]['id']
|
|
|
_logger.info(f"🔄 REUSING existing folder '{folder_name}' (ID: {folder_id})")
|
|
_logger.info(f"🔄 REUSING existing folder '{folder_name}' (ID: {folder_id})")
|
|
|
else:
|
|
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
|
|
return folder_id, was_reused
|
|
|
|
|
|
|
@@ -334,14 +370,13 @@ class CrmLead(models.Model):
|
|
|
|
|
|
|
|
def _store_folder_info(self, folder_id):
|
|
def _store_folder_info(self, folder_id):
|
|
|
"""Store folder information and update URL"""
|
|
"""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
|
|
# Copy URL to configured field if empty
|
|
|
if not self._get_configured_field_value():
|
|
if not self._get_configured_field_value():
|
|
@@ -357,10 +392,7 @@ class CrmLead(models.Model):
|
|
|
# Check if we need to create folder
|
|
# Check if we need to create folder
|
|
|
if self._should_create_folder(vals) and self._validate_folder_creation_prerequisites():
|
|
if self._should_create_folder(vals) and self._validate_folder_creation_prerequisites():
|
|
|
self._create_google_drive_folder_structure()
|
|
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 we have a folder, verify and update structure
|
|
|
if self.google_drive_folder_id:
|
|
if self.google_drive_folder_id:
|
|
@@ -368,10 +400,7 @@ class CrmLead(models.Model):
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
_logger.error(f"Error processing Google Drive updates for record {self.id}: {str(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):
|
|
def _should_create_folder(self, vals):
|
|
|
"""Helper method to determine if folder should be created"""
|
|
"""Helper method to determine if folder should be created"""
|
|
@@ -406,21 +435,41 @@ class CrmLead(models.Model):
|
|
|
self._rename_entire_folder_structure()
|
|
self._rename_entire_folder_structure()
|
|
|
|
|
|
|
|
# Update stored 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
|
|
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.)
|
|
# 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
|
|
current_structure = (old_values.get('google_drive_folder_name', '') if old_values
|
|
|
else self.google_drive_folder_name or '')
|
|
else self.google_drive_folder_name or '')
|
|
@@ -430,65 +479,106 @@ class CrmLead(models.Model):
|
|
|
self._rename_entire_folder_structure(new_components=expected_components)
|
|
self._rename_entire_folder_structure(new_components=expected_components)
|
|
|
|
|
|
|
|
# Update stored structure
|
|
# 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):
|
|
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:
|
|
if not self.google_drive_folder_id:
|
|
|
return
|
|
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:
|
|
|
|
|
+ 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
|
|
|
|
|
+
|
|
|
|
|
+ # 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}")
|
|
|
|
|
|
|
|
- self.with_context(skip_google_drive_update=True).write({
|
|
|
|
|
- 'google_drive_folder_name': new_components['opportunity_name']
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ # 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._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
|
|
|
|
|
+
|
|
|
|
|
+ # 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
|
|
|
|
|
+ self._update_folder_structure_fields(structure_components=new_components)
|
|
|
|
|
+
|
|
|
|
|
+ _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):
|
|
def _move_folder_to_new_company(self, new_company_id):
|
|
|
"""Move only this opportunity's folder to new company"""
|
|
"""Move only this opportunity's folder to new company"""
|
|
@@ -499,17 +589,21 @@ class CrmLead(models.Model):
|
|
|
new_root_folder_id = new_company.google_drive_crm_folder_id
|
|
new_root_folder_id = new_company.google_drive_crm_folder_id
|
|
|
|
|
|
|
|
if not new_root_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'
|
|
|
|
|
- )
|
|
|
|
|
- return
|
|
|
|
|
|
|
+ self._post_folder_message("⚠️ No se puede mover: Nueva empresa no tiene Google Drive configurado.")
|
|
|
|
|
+ return
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
drive_service = self.env['google.drive.service']
|
|
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._post_folder_message("⚠️ No se puede mover: La carpeta actual no existe o no es accesible.")
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
# Get current opportunity folder components
|
|
# 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
|
|
# 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'])
|
|
new_primary_folder_id, _ = self._create_or_get_folder_crm(new_root_folder_id, current_components['primary_name'])
|
|
@@ -521,19 +615,17 @@ class CrmLead(models.Model):
|
|
|
raise UserError(_('Failed to move folder to new company: %s') % result.get('error', 'Unknown error'))
|
|
raise UserError(_('Failed to move folder to new company: %s') % result.get('error', 'Unknown error'))
|
|
|
|
|
|
|
|
# Update the stored folder ID to the new location
|
|
# 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:
|
|
except Exception as e:
|
|
|
_logger.error(f"Error moviendo folder: {str(e)}")
|
|
_logger.error(f"Error moviendo folder: {str(e)}")
|
|
|
- raise
|
|
|
|
|
|
|
+ # Don't raise the exception, just log it and continue
|
|
|
|
|
+ self._post_folder_message(f"⚠️ Error moviendo carpeta: {str(e)}")
|
|
|
|
|
|
|
|
# ============================================================================
|
|
# ============================================================================
|
|
|
# MÉTODOS ESPECÍFICOS DE CRM
|
|
# MÉTODOS ESPECÍFICOS DE CRM
|
|
@@ -543,20 +635,43 @@ class CrmLead(models.Model):
|
|
|
"""Find the primary folder (company/contact level) in the hierarchy"""
|
|
"""Find the primary folder (company/contact level) in the hierarchy"""
|
|
|
try:
|
|
try:
|
|
|
drive_service = self.env['google.drive.service']
|
|
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)
|
|
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:
|
|
for folder_info in hierarchy:
|
|
|
folder_name = folder_info.get('name', '')
|
|
folder_name = folder_info.get('name', '')
|
|
|
level = folder_info.get('level', 0)
|
|
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:
|
|
if level == 0:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
if not folder_name.isdigit() and folder_name not in ['Meets', 'Archivos cliente']:
|
|
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:
|
|
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
|
|
return None
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
_logger.error(f"Error finding primary folder (CRM): {str(e)}")
|
|
_logger.error(f"Error finding primary folder (CRM): {str(e)}")
|
|
@@ -573,49 +688,120 @@ class CrmLead(models.Model):
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
def _analyze_crm_folder_structure(self, folder_id):
|
|
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:
|
|
try:
|
|
|
drive_service = self.env['google.drive.service']
|
|
drive_service = self.env['google.drive.service']
|
|
|
|
|
|
|
|
|
|
+ # Step 1: Validate the current folder
|
|
|
validation = drive_service.validate_folder_id(folder_id)
|
|
validation = drive_service.validate_folder_id(folder_id)
|
|
|
if not validation.get('valid'):
|
|
if not validation.get('valid'):
|
|
|
|
|
+ _logger.warning(f"Folder {folder_id} is not valid or accessible")
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
current_name = validation.get('name', '')
|
|
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})")
|
|
|
|
|
+
|
|
|
|
|
+ # 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 not hierarchy:
|
|
|
|
|
+ _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}
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- hierarchy = drive_service.navigate_folder_hierarchy(folder_id, max_levels=5)
|
|
|
|
|
|
|
+ # 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:
|
|
for folder_info in hierarchy:
|
|
|
folder_name = folder_info.get('name', '')
|
|
folder_name = folder_info.get('name', '')
|
|
|
level = folder_info.get('level', 0)
|
|
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,
|
|
'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:
|
|
except Exception as e:
|
|
|
_logger.error(f"Error in _analyze_crm_folder_structure: {str(e)}")
|
|
_logger.error(f"Error in _analyze_crm_folder_structure: {str(e)}")
|
|
|
return None
|
|
return None
|
|
@@ -625,29 +811,29 @@ class CrmLead(models.Model):
|
|
|
# ============================================================================
|
|
# ============================================================================
|
|
|
|
|
|
|
|
@api.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):
|
|
def write(self, vals):
|
|
|
"""Override write method to handle Google Drive folder updates"""
|
|
"""Override write method to handle Google Drive folder updates"""
|
|
@@ -730,13 +916,10 @@ class CrmLead(models.Model):
|
|
|
raise UserError(_('Google Drive CRM folder is not configured for this company.'))
|
|
raise UserError(_('Google Drive CRM folder is not configured for this company.'))
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
- expected_components = self._get_folder_name_components()
|
|
|
|
|
|
|
+ expected_components = self._get_current_folder_components()
|
|
|
self._rename_entire_folder_structure(new_components=expected_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 {
|
|
return {
|
|
|
'type': 'ir.actions.client',
|
|
'type': 'ir.actions.client',
|
|
@@ -760,11 +943,50 @@ class CrmLead(models.Model):
|
|
|
raise UserError(_('No Google Drive folder exists for this opportunity.'))
|
|
raise UserError(_('No Google Drive folder exists for this opportunity.'))
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
- expected_components = self._get_folder_name_components()
|
|
|
|
|
|
|
+ # 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_current_folder_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)
|
|
current_structure = self._analyze_crm_folder_structure(self.google_drive_folder_id)
|
|
|
|
|
+ _logger.info(f"📊 Current structure analysis: {current_structure}")
|
|
|
|
|
|
|
|
if not current_structure:
|
|
if not current_structure:
|
|
|
- raise UserError(_('Could not analyze current folder 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/>"
|
|
|
|
|
+ error_msg += f"• Folder may be in a Shared Drive without proper access<br/>"
|
|
|
|
|
+ error_msg += f"• Network connectivity issues<br/><br/>"
|
|
|
|
|
+ error_msg += f"<strong>Expected Structure:</strong><br/>"
|
|
|
|
|
+ error_msg += f"📁 [Root Folder] (MC Team)<br/>"
|
|
|
|
|
+ error_msg += f"└── 📁 {expected_components['primary_name']} (Company/Contact)<br/>"
|
|
|
|
|
+ error_msg += f" └── 📁 {expected_components['year']} (Year)<br/>"
|
|
|
|
|
+ error_msg += f" └── 📁 {expected_components['opportunity_name']} (Opportunity)<br/>"
|
|
|
|
|
+ error_msg += f" ├── 📁 Meets<br/>"
|
|
|
|
|
+ error_msg += f" └── 📁 Archivos cliente<br/><br/>"
|
|
|
|
|
+ error_msg += f"<strong>Recommendation:</strong> Try using the 'Rename Folder Structure' button to recreate the structure."
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ 'type': 'ir.actions.client',
|
|
|
|
|
+ 'tag': 'display_notification',
|
|
|
|
|
+ 'params': {
|
|
|
|
|
+ 'title': _('Folder Structure Analysis'),
|
|
|
|
|
+ 'message': error_msg,
|
|
|
|
|
+ 'type': 'warning',
|
|
|
|
|
+ 'sticky': True,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
analysis = self._compare_folder_structures(expected_components, current_structure)
|
|
analysis = self._compare_folder_structures(expected_components, current_structure)
|
|
|
|
|
|
|
@@ -779,72 +1001,119 @@ class CrmLead(models.Model):
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
|
|
+ _logger.error(f"Error in action_analyze_folder_structure: {str(e)}")
|
|
|
raise UserError(_('Failed to analyze folder structure: %s') % str(e))
|
|
raise UserError(_('Failed to analyze folder structure: %s') % str(e))
|
|
|
|
|
|
|
|
def _compare_folder_structures(self, expected_components, current_structure):
|
|
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
|
|
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
|
|
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
|
|
# MÉTODOS DE SINCRONIZACIÓN DE MEETS
|