diff --git a/app/system/backend/sync_router.py b/app/system/backend/sync_router.py index 2943a8e..3e2a068 100644 --- a/app/system/backend/sync_router.py +++ b/app/system/backend/sync_router.py @@ -163,166 +163,162 @@ async def sync_from_vtiger() -> Dict[str, Any]: @router.post("/sync/vtiger-contacts") async def sync_vtiger_contacts() -> Dict[str, Any]: """ - Sync contacts from vTiger Contacts module - Links to existing Hub customers by Account ID + SIMPEL TILGANG - Sync contacts from vTiger and link to customers + Step 1: Fetch all contacts from vTiger + Step 2: Create/update contact in Hub + Step 3: Link to customer IF account_id matches customer.vtiger_id """ try: - logger.info("πŸ”„ Starting vTiger contacts sync...") + logger.info("πŸ”„ Starting vTiger contacts sync (SIMPEL VERSION)...") vtiger = get_vtiger_service() - # Fetch ALL contacts with pagination (same as accounts) + # ===== STEP 1: FETCH ALL CONTACTS ===== + logger.info("πŸ“₯ STEP 1: Fetching contacts from vTiger...") all_contacts = [] last_id = None batch_size = 100 while True: - # Build query with ID filter to paginate if last_id is None: query = f"SELECT id, firstname, lastname, email, phone, mobile, title, department, account_id FROM Contacts ORDER BY id LIMIT {batch_size};" else: query = f"SELECT id, firstname, lastname, email, phone, mobile, title, department, account_id FROM Contacts WHERE id > '{last_id}' ORDER BY id LIMIT {batch_size};" batch = await vtiger.query(query) - if not batch or len(batch) == 0: break all_contacts.extend(batch) last_id = batch[-1].get('id') - logger.info(f"πŸ“₯ Fetched batch: {len(batch)} contacts (last ID: {last_id}, total: {len(all_contacts)})") + logger.info(f" β†’ Batch: {len(batch)} contacts (ID: {last_id}, total: {len(all_contacts)})") if len(batch) < batch_size: break - logger.info(f"πŸ“₯ Fetched total of {len(all_contacts)} contacts from vTiger") - contacts = all_contacts + logger.info(f"βœ… STEP 1 DONE: {len(all_contacts)} contacts fetched") + + # ===== STEP 2 & 3: PROCESS EACH CONTACT ===== + logger.info("πŸ”„ STEP 2 & 3: Creating/updating contacts and linking to customers...") created_count = 0 updated_count = 0 skipped_count = 0 linked_count = 0 - debug_counter = 0 # Track how many we've logged + debug_count = 0 - for contact in contacts: - vtiger_contact_id = contact.get('id') - first_name = contact.get('firstname', '').strip() - last_name = contact.get('lastname', '').strip() + for i, contact in enumerate(all_contacts, 1): + vtiger_id = contact.get('id') + first_name = (contact.get('firstname') or '').strip() + last_name = (contact.get('lastname') or '').strip() + email = contact.get('email') + account_id = contact.get('account_id') # This is the vTiger Account ID - if not (first_name or last_name): + # Show progress every 100 contacts + if i % 100 == 0: + logger.info(f" β†’ Progress: {i}/{len(all_contacts)} contacts processed...") + + # Must have name + if not first_name and not last_name: skipped_count += 1 - logger.debug(f"⏭️ Sprunget over: Intet navn (ID: {vtiger_contact_id})") continue - # Find existing contact by vTiger ID - existing = None - if vtiger_contact_id: - existing = execute_query( - "SELECT id FROM contacts WHERE vtiger_id = %s", - (vtiger_contact_id,) - ) + # --- STEP 2A: Check if contact exists --- + existing = execute_query( + "SELECT id FROM contacts WHERE vtiger_id = %s", + (vtiger_id,) + ) if vtiger_id else None - contact_data = { - 'first_name': first_name, - 'last_name': last_name, - 'email': contact.get('email'), - 'phone': contact.get('phone'), - 'mobile': contact.get('mobile'), - 'title': contact.get('title'), - 'department': contact.get('department'), - 'vtiger_id': vtiger_contact_id - } + # --- STEP 2B: Create or update contact --- + contact_id = None if existing: - # Update existing contact - update_query = """ + # UPDATE existing + execute_query(""" UPDATE contacts - SET first_name = %s, last_name = %s, email = %s, phone = %s, - mobile = %s, title = %s, department = %s, updated_at = NOW() + SET first_name = %s, last_name = %s, email = %s, + phone = %s, mobile = %s, title = %s, department = %s, + updated_at = NOW() WHERE id = %s - """ - execute_query(update_query, ( - contact_data['first_name'], - contact_data['last_name'], - contact_data['email'], - contact_data['phone'], - contact_data['mobile'], - contact_data['title'], - contact_data['department'], + """, ( + first_name, last_name, email, + contact.get('phone'), contact.get('mobile'), + contact.get('title'), contact.get('department'), existing[0]['id'] )) - updated_count += 1 contact_id = existing[0]['id'] - logger.info(f"✏️ Opdateret kontakt: {first_name} {last_name} (Email: {contact_data['email'] or 'ingen'})") + updated_count += 1 else: - # Create new contact - insert_query = """ + # CREATE new + result = execute_query(""" INSERT INTO contacts (first_name, last_name, email, phone, mobile, title, department, vtiger_id) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING id - """ - result = execute_query(insert_query, ( - contact_data['first_name'], - contact_data['last_name'], - contact_data['email'], - contact_data['phone'], - contact_data['mobile'], - contact_data['title'], - contact_data['department'], - contact_data['vtiger_id'] + """, ( + first_name, last_name, email, + contact.get('phone'), contact.get('mobile'), + contact.get('title'), contact.get('department'), + vtiger_id )) - if result: - created_count += 1 + if result and len(result) > 0: contact_id = result[0]['id'] - logger.info(f"✨ Oprettet kontakt: {first_name} {last_name} (Email: {contact_data['email'] or 'ingen'})") + created_count += 1 else: skipped_count += 1 - logger.warning(f"⚠️ Kunne ikke oprette kontakt: {first_name} {last_name}") continue - # Link contact to customer if account_id exists - account_id = contact.get('account_id') - - # ALWAYS log first 20 for debugging - if debug_counter < 20: - logger.warning(f"πŸ” DEBUG #{debug_counter+1}: name={first_name} {last_name}, account_id='{account_id}', has_contact_id={contact_id is not None if 'contact_id' in locals() else False}") - debug_counter += 1 + # --- DEBUG LOGGING FOR FIRST 20 CONTACTS --- + if debug_count < 20: + logger.warning(f"πŸ” DEBUG {debug_count+1}: Contact '{first_name} {last_name}' (vtiger_id={vtiger_id}, contact_id={contact_id}, account_id={account_id})") + debug_count += 1 + # --- STEP 3: LINK TO CUSTOMER --- if not account_id: - continue # Skip if no account_id - - if not contact_id: - logger.warning(f"⚠️ Mangler contact_id for {first_name} {last_name}") + # No account_id means contact is not linked to any account in vTiger + if debug_count <= 20: + logger.warning(f" ↳ No account_id - cannot link") continue - # Find customer by vTiger account ID - customer = execute_query( + # Find Hub customer with matching vtiger_id + customer_result = execute_query( "SELECT id, name FROM customers WHERE vtiger_id = %s", (account_id,) ) - if customer: - customer_name = customer[0]['name'] - # Check if relationship exists - existing_rel = execute_query( - "SELECT id FROM contact_companies WHERE contact_id = %s AND customer_id = %s", - (contact_id, customer[0]['id']) - ) - - if not existing_rel: - # Create relationship - execute_query( - "INSERT INTO contact_companies (contact_id, customer_id, is_primary) VALUES (%s, %s, false)", - (contact_id, customer[0]['id']) - ) - linked_count += 1 - logger.info(f"πŸ”— Linket kontakt {first_name} {last_name} til firma: {customer_name}") - else: - logger.info(f"⚠️ Kunde ikke fundet for account_id={account_id} (kontakt: {first_name} {last_name})") + if not customer_result or len(customer_result) == 0: + # No matching customer found + if debug_count <= 20: + logger.warning(f" ↳ No customer found with vtiger_id={account_id}") + continue + + customer_id = customer_result[0]['id'] + customer_name = customer_result[0]['name'] + + # Check if link already exists + existing_link = execute_query( + "SELECT id FROM contact_companies WHERE contact_id = %s AND customer_id = %s", + (contact_id, customer_id) + ) + + if existing_link: + # Already linked + if debug_count <= 20: + logger.warning(f" ↳ Already linked to '{customer_name}'") + continue + + # CREATE LINK + execute_query( + "INSERT INTO contact_companies (contact_id, customer_id, is_primary) VALUES (%s, %s, false)", + (contact_id, customer_id) + ) + linked_count += 1 + + if linked_count <= 10: # Log first 10 successful links + logger.info(f"πŸ”— LINKED: {first_name} {last_name} β†’ {customer_name}") - logger.info(f"βœ… vTiger kontakt sync fuldfΓΈrt: {created_count} oprettet, {updated_count} opdateret, {linked_count} linket, {skipped_count} sprunget over af {len(contacts)} totalt") + logger.info(f"βœ… SYNC COMPLETE: created={created_count}, updated={updated_count}, linked={linked_count}, skipped={skipped_count}, total={len(all_contacts)}") return { "status": "success", @@ -330,11 +326,11 @@ async def sync_vtiger_contacts() -> Dict[str, Any]: "updated": updated_count, "linked": linked_count, "skipped": skipped_count, - "total_processed": len(contacts) + "total_processed": len(all_contacts) } except Exception as e: - logger.error(f"❌ vTiger contacts sync error: {e}") + logger.error(f"❌ vTiger contacts sync error: {e}", exc_info=True) raise HTTPException(status_code=500, detail=str(e))