|
|
@@ -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
|