Leads

Aynı telefon numarasıyla ikinci kez gelen lead'leri elle karşılaştırmadan tek kayda indirir; reklam formu, WhatsApp ve web sitesinden ayrı ayrı gelen kayıtlar tek lead'de toplanır.

Modül
Leads
Tetik
Workflow · On Create · On Edit (onay kutusu)
Argüman
leadId = Lead Id
Uzunluk
300 satır Deluge
Güncelleme

Ne yapar

Tetiklenen kaydı master kabul eder. Phone ve Mobile alanlarının kayıtlı değeriyle CRM'de telefon araması yapar. Sonra hem master'ın hem bulunan kayıtların numaralarını rakama indirir (uzunluk tutarsa 0090 / 90 / 0 önekini atar) ve yalnız bu anahtarda tam eşleşenleri mükerrer sayar. Zoho'nun telefon araması ülke kodu ve boşluk farkını tolere eder: canlı testte 0532 111 22 33 ile yapılan arama +905321112233 olarak kaydedilmiş lead'i de buldu. Başka biçimleri kendi verinizle deneyin. Her mükerreri Zoho'nun Merge API'siyle master'a katar: master'da boş olan alanlar child'dan doldurulur, doluysa master'ınki kalır. Zoho birleştirmeyi zamanlarsa (SCHEDULED) döngü durur; kalanlar kutu yeniden işaretlenince birleşir.

Kurulum mantığı

  1. 01

    Setup içinde Developer Hub, Connections altında Zoho CRM bağlantısı. Kodun kullandığı ad bağlantının Link Name değeridir ve crm_oauth_v8 olmalı (koddaki connection: ile birebir); oluşturduktan sonra açılan sayfada kontrol edin. Scope ZohoCRM.modules.ALL ve Zoho'nun arama API'sinin istediği ZohoSearch.securesearch.READ. Bağlantı bu adla kurulmadan Zoho kodu kaydetmez, derleme hatası verir. Arama ve merge bu bağlantıyla gider; zoho.crm.* yerleşik çağrıları bağlantı istemez.

  2. 02

    Otomatik için Workflow On Create: yeni gelen kayıt master olur, eskiler ona katılır. On Create kurulsa da 3. adımdaki onay kutusu kuralını da kurun; 200 sınırında ve SCHEDULED durumunda kalanlar yalnız onunla birleşir.

  3. 03

    Elle çalıştırmak için Workflow On Edit, onay kutusuyla: modüle "Mükerrerleri Birleştir" adlı bir onay kutusu alanı ekleyin. Kural On Edit, "Repeat this workflow whenever a Lead is edited" açık, koşul "Mükerrerleri Birleştir = seçili"; eylemler Function ve kutuyu yeniden boşaltan bir Field Update. Temsilci kutuyu işaretleyip kaydettiğinde o kayıt master olur. Custom Button bu void automation imzasıyla bağlanamaz.

  4. 04

    Hangi kuralı kurarsanız kurun argüman leadId, Lead Id alanına eşlenir.

  5. 05

    Veri merkezi: URL'ler zohoapis.eu; org ABD'deyse zohoapis.com, Hindistan'daysa zohoapis.in yazılır.

Alan eşlemesi

Koda yazılan alanlar ve kendi org'unuzda dikkat etmeniz gerekenler.

AlanDeğerNot
Phone, MobileokunurAramada kayıtlı değer olduğu gibi kullanılır; karşılaştırma için rakam dışı atılır, uzunluk tutarsa 0090/90/0 öneki kırpılır.
id (bulunan)karşılaştırılırMaster'ın kendisi ve tekrarlar elenir.
_fields17 standart alanSalutation, First_Name, Last_Name, Company, Email, Secondary_Email, Phone, Mobile, Fax, Designation, Website, Street, City, State, Zip_Code, Country, Description. Master'da boş olanlar child'dan alınır.
Merge APIPOST /crm/v8/Leads/{master}/actions/mergeİstek başına tek child.

Deluge kodu

Kodu olduğu gibi kopyalayın; değiştirmeniz gereken yerler yukarıdaki tabloda işaretli. İzle düğmesi, fonksiyonu adım adım okutur.

void automation.mergeLeadsByPhoneAndMobile(String leadId)
void automation.mergeLeadsByPhoneAndMobile(String leadId)
{
try
{
	info "===== Lead Mükerrer Birleştirme Başladı =====";
	info "Master Lead ID: " + leadId;

	if(leadId == null || leadId.trim() == "")
	{
		info "Lead ID boş.";
		return;
	}

	leadId = leadId.trim();
	masterLead = zoho.crm.getRecordById("Leads",leadId.toLong());

	if(masterLead == null || masterLead.isEmpty())
	{
		info "Master Lead bulunamadı.";
		return;
	}

	searchPhones = List();
	masterPhoneKeys = List();

	if(masterLead.get("Phone") != null && masterLead.get("Phone").toString().trim() != "")
	{
		searchPhones.add(masterLead.get("Phone").toString().trim());
	}

	if(masterLead.get("Mobile") != null && masterLead.get("Mobile").toString().trim() != "")
	{
		mobileValue = masterLead.get("Mobile").toString().trim();

		if(!searchPhones.contains(mobileValue))
		{
			searchPhones.add(mobileValue);
		}
	}

	if(searchPhones.size() == 0)
	{
		info "Phone ve Mobile alanları boş.";
		return;
	}

	for each searchPhone in searchPhones
	{
		phoneKey = searchPhone.replaceAll("[^0-9]","");

		if(phoneKey.startsWith("0090") && phoneKey.length() == 14)
		{
			phoneKey = phoneKey.subString(4,phoneKey.length());
		}
		else if(phoneKey.startsWith("90") && phoneKey.length() == 12)
		{
			phoneKey = phoneKey.subString(2,phoneKey.length());
		}
		else if(phoneKey.startsWith("0") && phoneKey.length() == 11)
		{
			phoneKey = phoneKey.subString(1,phoneKey.length());
		}

		if(phoneKey != "" && !masterPhoneKeys.contains(phoneKey))
		{
			masterPhoneKeys.add(phoneKey);
		}
	}

	duplicateIds = List();

	for each searchPhone in searchPhones
	{
		try
		{
			searchParams = Map();
			searchParams.put("phone",searchPhone);
			searchParams.put("page",1);
			searchParams.put("per_page",200);

			searchResponse = invokeurl
			[
				url :"https://www.zohoapis.eu/crm/v8/Leads/search"
				type :GET
				parameters:searchParams
				connection:"crm_oauth_v8"
			];

			if(searchResponse != null && searchResponse.get("data") != null)
			{
				for each foundLead in searchResponse.get("data")
				{
					foundId = ifnull(foundLead.get("id"),"").toString();

					foundPhoneKey = ifnull(foundLead.get("Phone"),"").toString().replaceAll("[^0-9]","");
					foundMobileKey = ifnull(foundLead.get("Mobile"),"").toString().replaceAll("[^0-9]","");

					if(foundPhoneKey.startsWith("0090") && foundPhoneKey.length() == 14)
					{
						foundPhoneKey = foundPhoneKey.subString(4,foundPhoneKey.length());
					}
					else if(foundPhoneKey.startsWith("90") && foundPhoneKey.length() == 12)
					{
						foundPhoneKey = foundPhoneKey.subString(2,foundPhoneKey.length());
					}
					else if(foundPhoneKey.startsWith("0") && foundPhoneKey.length() == 11)
					{
						foundPhoneKey = foundPhoneKey.subString(1,foundPhoneKey.length());
					}

					if(foundMobileKey.startsWith("0090") && foundMobileKey.length() == 14)
					{
						foundMobileKey = foundMobileKey.subString(4,foundMobileKey.length());
					}
					else if(foundMobileKey.startsWith("90") && foundMobileKey.length() == 12)
					{
						foundMobileKey = foundMobileKey.subString(2,foundMobileKey.length());
					}
					else if(foundMobileKey.startsWith("0") && foundMobileKey.length() == 11)
					{
						foundMobileKey = foundMobileKey.subString(1,foundMobileKey.length());
					}

					exactMatch = false;

					if(foundPhoneKey != "" && masterPhoneKeys.contains(foundPhoneKey))
					{
						exactMatch = true;
					}

					if(foundMobileKey != "" && masterPhoneKeys.contains(foundMobileKey))
					{
						exactMatch = true;
					}

					if(foundId != "" && foundId != leadId && exactMatch && !duplicateIds.contains(foundId))
					{
						duplicateIds.add(foundId);
					}
				}
			}
		}
		catch (searchError)
		{
			info "Telefon arama hatası: " + searchError.toString();
		}
	}

	if(duplicateIds.size() == 0)
	{
		info "Birleştirilecek mükerrer Lead bulunamadı.";
		return;
	}

	info "Mükerrer Lead ID'leri: " + duplicateIds;

	transferFields = List();
	transferFields.add("Salutation");
	transferFields.add("First_Name");
	transferFields.add("Last_Name");
	transferFields.add("Company");
	transferFields.add("Email");
	transferFields.add("Secondary_Email");
	transferFields.add("Phone");
	transferFields.add("Mobile");
	transferFields.add("Fax");
	transferFields.add("Designation");
	transferFields.add("Website");
	transferFields.add("Street");
	transferFields.add("City");
	transferFields.add("State");
	transferFields.add("Zip_Code");
	transferFields.add("Country");
	transferFields.add("Description");

	requestHeaders = Map();
	requestHeaders.put("Content-Type","application/json");

	mergedCount = 0;
	mergeCanContinue = true;

	for each childId in duplicateIds
	{
		if(mergeCanContinue)
		{
			currentMaster = zoho.crm.getRecordById("Leads",leadId.toLong());
			childLead = zoho.crm.getRecordById("Leads",childId.toLong());

			if(currentMaster == null || currentMaster.isEmpty())
			{
				info "Master Lead bulunamadığı için işlem durduruldu.";
				mergeCanContinue = false;
			}
			else if(childLead == null || childLead.isEmpty())
			{
				info "Child Lead bulunamadı: " + childId;
			}
			else
			{
				fieldList = List();

				for each fieldApiName in transferFields
				{
					masterValue = currentMaster.get(fieldApiName);
					childValue = childLead.get(fieldApiName);

					if((masterValue == null || masterValue.toString().trim() == "") && childValue != null && childValue.toString().trim() != "")
					{
						fieldMap = Map();
						fieldMap.put("api_name",fieldApiName);
						fieldList.add(fieldMap);
					}
				}

				if(fieldList.size() == 0)
				{
					for each fieldApiName in transferFields
					{
						if(fieldList.size() == 0)
						{
							masterValue = currentMaster.get(fieldApiName);
							childValue = childLead.get(fieldApiName);

							if(masterValue != null && childValue != null && masterValue.toString().trim() != "" && masterValue.toString().trim().toLowerCase() == childValue.toString().trim().toLowerCase())
							{
								fieldMap = Map();
								fieldMap.put("api_name",fieldApiName);
								fieldList.add(fieldMap);
							}
						}
					}
				}

				if(fieldList.size() == 0)
				{
					info "Güvenli aktarım alanı bulunamadı; merge atlandı: " + childId;
				}
				else
				{
					childMap = Map();
					childMap.put("id",childId);
					childMap.put("_fields",fieldList);

					dataList = List();
					dataList.add(childMap);

					mergeObject = Map();
					mergeObject.put("data",dataList);

					mergeList = List();
					mergeList.add(mergeObject);

					payload = Map();
					payload.put("merge",mergeList);

					try
					{
						mergeResponse = invokeurl
						[
							url :"https://www.zohoapis.eu/crm/v8/Leads/" + leadId + "/actions/merge"
							type :POST
							body:payload.toString()
							headers:requestHeaders
							connection:"crm_oauth_v8"
						];

						info "Merge response: " + mergeResponse;

						if(mergeResponse != null && mergeResponse.get("merge") != null && mergeResponse.get("merge").size() > 0)
						{
							mergeResult = mergeResponse.get("merge").get(0);
							mergeCode = ifnull(mergeResult.get("code"),"").toString();

							if(mergeCode == "SUCCESS")
							{
								mergedCount = mergedCount + 1;
							}
							else if(mergeCode == "SCHEDULED")
							{
								info "Merge zamanlandı. Kalan kayıtlar sonraki çalışmaya bırakıldı.";
								mergeCanContinue = false;
							}
						}
					}
					catch (mergeError)
					{
						info "Merge hatası | Child ID: " + childId + " | " + mergeError.toString();
					}
				}
			}
		}
	}

	info "Başarılı merge sayısı: " + mergedCount;
}
catch (e)
{
	info "Genel hata: " + e.toString();
}
}
10 ADIMİzle düğmesi fonksiyonu adım adım aydınlatır.

Dikkat edilecekler

  • Master = tetiklenen kayıt

    Argümandaki lead kalır, bulunan mükerrerler onun içine katılıp silinir. On Create'e bağlanırsa en yeni kayıt kazanır; eski kaydın notları, görevleri ve aramaları master'a taşınır ama kayıt tarihi yeni olur. Eski kaydın kalması isteniyorsa On Create kuralını kurmayın; yalnız onay kutusu kuralını kurup kutuyu eski kayıtta işaretleyin.

  • Birleştirme geri alınamaz

    Zoho merge child kaydı siler ve belgelerinde birleştirmeyi geri alan bir yol tanımlamaz; test için önce sandbox ya da onay kutusuyla elle, sonra otomatik tetik.

  • `phone` araması geniş tarar

    Arama parametresi Leads modülündeki Phone, Mobile, Fax ve özel telefon alanları dahil tüm telefon tipi alanlarda eşleşir; bu yüzden kod bulunanları yeniden normalize edip yalnız Phone/Mobile tam eşleşmesini kabul eder. Faks numarası aynı olan iki firma birleşmez.

  • 200 kayıt sınırı

    Arama tek sayfa okur, her numara için en fazla 200 sonuç döner. Fazlası kendiliğinden işlenmez; On Create aynı kayıt için bir daha tetiklenmez. Kalanlar, aynı master'da onay kutusu yeniden işaretlenince yakalanır. Aynı numarayla yeni bir lead gelirse de yakalanır, ama bu kez master yeni lead olur.

  • Kutu boşaltılmalı

    Kural yalnız kutu işaretliyken çalışır, sıradan düzenleme birleştirme yapmaz. Kutuyu boşaltan Field Update eylemi unutulursa kutu işaretli kaldıkça her düzenleme arama ve birleştirmeyi yeniden çalıştırır.

  • `_fields` boş gönderilmez

    Merge API child için en az bir alan bekler; master'da hiç boş alan yoksa kod iki kayıtta aynı olan bir alanı gönderir, o da yoksa o child'ı atlar ve loglar.

  • SCHEDULED

    Birleştirilecek child kaydın ilişkili listelerinden birinde 1000'den fazla kayıt varsa Zoho o birleştirmeyi arka plana alır, SCHEDULED döner ve zamanlanan birleştirmeyi kendisi tamamlar. Kod o anda döngüyü durdurur, kalan mükerrerlere dokunmaz ve yeniden denemez. On Create aynı kayıt için bir daha tetiklenmez. Arka plan işi bittikten sonra master kayıtta onay kutusunu yeniden işaretleyin; iş numarası (job_id) log'daki "Merge response" satırında görünür.

İlgili kayıtlar

  1. 05
    Leads · Tasks

    Lead'in tüm açık görevlerini kapat

    Lead kaybedildiğinde ya da nitelikli bulunmadığında üzerinde kalan tüm açık görevleri tek seferde tamamlandı yapar; temsilcinin görev listesinde ölü lead'lerin hatırlatmaları birikmez.

    Workflow · Alan değişimi72 satır Deluge
  2. 06
    Calls · Leads

    Lead'i arayan temsilciye ata

    Lead'e bağlı bir arama kaydedildiğinde ya da değiştirildiğinde lead'i o aramanın sahibine atar; lead, en son kaydedilen ya da değiştirilen aramanın sahibinde kalır.

    Workflow · Calls · arama türü86 satır Deluge
  3. 07
    Leads

    Lead telefonlarını +90 biçimine getir

    Lead'in Telefon ve Mobil alanlarında 05…, 5…, 90… ya da 0090… ile girilmiş numaraları, kayıt ya da düzenleme sonrası +90… biçimine çevirir; arama ve mesaj entegrasyonları bu numaraları tek biçimde alır.

    Workflow · On Create / On Edit107 satır Deluge

30+

proje

%99

müşteri memnuniyeti

3 kanal

kurulum sonrası destek

İletişim

Mevcut sisteminiz, hedefiniz ve takviminize göre somut bir teklif ve canlı demo hazırlıyoruz. Sözleşme öncesi taahhüt yok.

Görüşme planla

Bilgileriniz KVKK Aydınlatma Metni kapsamında işlenir. 24 saat içinde dönüş yapıyoruz.