Contacts

Aynı telefon ya da aynı e-postayla açılmış kişi kayıtlarını tek kayda indirir; lead sürümünün Contacts karşılığı, üstüne e-posta eşleşmesi ekler.

Modül
Contacts
Tetik
Workflow · On Create · On Edit (onay kutusu)
Argüman
contactId = Contact Id
Uzunluk
349 satır Deluge
Güncelleme

Ne yapar

Tetiklenen kaydı master kabul eder. Phone ve Mobile alanlarının kayıtlı değeriyle telefon araması, küçük harfe çevrilmiş Email ile e-posta araması yapar. Dönen kayıtların numaralarını rakama indirip (uzunluk tutarsa 0090 / 90 / 0 önekini atar) master'ınkilerle, e-postalarını küçük harfle karşılaştırır ve yalnız tam eşleşenleri mükerrer sayar. Zoho'nun telefon araması ülke kodu ve boşluk farkını tolere eder: canlı testte 0544 123 45 67 ile yapılan arama +905441234567 olarak kaydedilmiş kişiyi de buldu. Başka biçimleri kendi verinizle deneyin. Her mükerreri Merge API ile master'a katar: aktarım listesindeki 23 standart alandan master'da boş, child'da dolu olanlar child'dan doldurulur. Özel alanlar ve listede olmayan diğer alanlar child'dan alınmaz; bu alanlarda master'daki değer kalır. SCHEDULED dönerse 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.

  2. 02

    Otomatik için Workflow On Create: yeni kayıt master olur. 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 Contact 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 contactId, Contact Id alanına eşlenir.

  5. 05

    Veri merkezi: zohoapis.eu; ABD için zohoapis.com.

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.
EmailokunurKüçük harf tam eşleşme; yalnız master'ın Email değeri aranır ve bulunan kaydın Email alanıyla karşılaştırılır, Secondary_Email eşleşmesi mükerrer sayılmaz.
_fields23 standart alanHitap, ad, soyad, Email ve Secondary_Email, 4 telefon alanı (Phone, Mobile, Home_Phone, Other_Phone) ve Fax, unvan, bölüm, Mailing_* ve Other_* adresleri, Description. Master'da boş olanlar child'dan alınır.
Merge APIPOST /crm/v8/Contacts/{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.mergeContactsByPhoneMobileEmail(String contactId)
void automation.mergeContactsByPhoneMobileEmail(String contactId)
{
try
{
	info "===== Contact Mükerrer Birleştirme Başladı =====";
	info "Master Contact ID: " + contactId;

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

	contactId = contactId.trim();
	masterContact = zoho.crm.getRecordById("Contacts",contactId.toLong());

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

	searchPhones = List();
	masterPhoneKeys = List();
	masterEmail = "";

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

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

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

	if(masterContact.get("Email") != null && masterContact.get("Email").toString().trim() != "")
	{
		masterEmail = masterContact.get("Email").toString().trim().toLowerCase();
	}

	if(searchPhones.size() == 0 && masterEmail == "")
	{
		info "Phone, Mobile ve Email 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/Contacts/search"
				type :GET
				parameters:searchParams
				connection:"crm_oauth_v8"
			];

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

					foundPhoneKey = ifnull(foundContact.get("Phone"),"").toString().replaceAll("[^0-9]","");
					foundMobileKey = ifnull(foundContact.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 != contactId && exactMatch && !duplicateIds.contains(foundId))
					{
						duplicateIds.add(foundId);
					}
				}
			}
		}
		catch (searchError)
		{
			info "Telefon arama hatası: " + searchError.toString();
		}
	}

	if(masterEmail != "")
	{
		try
		{
			emailParams = Map();
			emailParams.put("email",masterEmail);
			emailParams.put("page",1);
			emailParams.put("per_page",200);

			emailResponse = invokeurl
			[
				url :"https://www.zohoapis.eu/crm/v8/Contacts/search"
				type :GET
				parameters:emailParams
				connection:"crm_oauth_v8"
			];

			if(emailResponse != null && emailResponse.get("data") != null)
			{
				for each foundContact in emailResponse.get("data")
				{
					foundId = ifnull(foundContact.get("id"),"").toString();
					foundEmail = ifnull(foundContact.get("Email"),"").toString().trim().toLowerCase();

					if(foundId != "" && foundId != contactId && foundEmail == masterEmail && !duplicateIds.contains(foundId))
					{
						duplicateIds.add(foundId);
					}
				}
			}
		}
		catch (emailSearchError)
		{
			info "Email arama hatası: " + emailSearchError.toString();
		}
	}

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

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

	transferFields = List();
	transferFields.add("Salutation");
	transferFields.add("First_Name");
	transferFields.add("Last_Name");
	transferFields.add("Email");
	transferFields.add("Secondary_Email");
	transferFields.add("Phone");
	transferFields.add("Mobile");
	transferFields.add("Home_Phone");
	transferFields.add("Other_Phone");
	transferFields.add("Fax");
	transferFields.add("Title");
	transferFields.add("Department");
	transferFields.add("Mailing_Street");
	transferFields.add("Mailing_City");
	transferFields.add("Mailing_State");
	transferFields.add("Mailing_Zip");
	transferFields.add("Mailing_Country");
	transferFields.add("Other_Street");
	transferFields.add("Other_City");
	transferFields.add("Other_State");
	transferFields.add("Other_Zip");
	transferFields.add("Other_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("Contacts",contactId.toLong());
			childContact = zoho.crm.getRecordById("Contacts",childId.toLong());

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

				for each fieldApiName in transferFields
				{
					masterValue = currentMaster.get(fieldApiName);
					childValue = childContact.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 = childContact.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/Contacts/" + contactId + "/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();
}
}
11 ADIMİzle düğmesi fonksiyonu adım adım aydınlatır.

Dikkat edilecekler

  • Master = tetiklenen kayıt

    On Create'te en yeni kişi kalır, eskiler ona katılır. Kişiye bağlı fırsatlar, faturalar ve aktiviteler master'a taşınır; kayıt tarihi ve sahip yeni kaydınki olur. Eski kaydın kalması gerekiyorsa On Create kuralını kurmayın; yalnız onay kutusu kuralını kurup kutuyu eski kayıtta işaretleyin.

  • Birleştirme geri alınamaz

    Child kayıt silinir ve Zoho belgelerinde birleştirmeyi geri alan bir yol tanımlanmaz; önce sandbox ya da onay kutusuyla elle.

  • Ortak e-posta tuzağı

    info@firma.com gibi ortak adresle açılmış farklı kişiler e-posta eşleşmesiyle birleşir. Böyle veri varsa e-posta aramasını (155-190) kapatın ya da tetik koşuluyla eleyin.

  • `phone` araması geniş tarar

    Fax ve Home_Phone dahil tüm telefon alanlarında eşleşir; kod yalnız Phone/Mobile tam eşleşmesini kabul eder.

  • 200 kayıt sınırı

    Her arama tek sayfa okur, en fazla 200 sonuç döner ve sayfalama yoktur. Bu 200 sonuca tam eşleşmeyen kayıtlar da girebilir. 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ı telefon ya da e-postayla yeni bir kişi gelirse de yakalanır, ama bu kez master yeni kişi 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 en az bir alan bekler; uygun alan yoksa o child atlanır ve loglanır.

  • 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. 09
    Contacts · Tasks

    Kişinin tüm açık görevlerini kapat

    Müşteri ilişkisi bittiğinde ya da süreç tamamlandığında kişi kaydındaki tüm açık görevleri tek seferde kapatır; Lead sürümünün Contacts modülü karşılığı.

    Workflow · Alan değişimi72 satır Deluge
  2. 10
    Contacts

    Kişi telefonlarını +90 biçimine getir

    Kişi kaydındaki Telefon ve Mobil alanlarında 05…, 5…, 90… ya da 0090… ile girilmiş numaraları +90… biçimine çevirir; WhatsApp, SMS ve santral entegrasyonları bu numaraları tek biçimde alır.

    Workflow · On Create / On Edit107 satır Deluge
  3. 13
    Contacts · Accounts

    Firma fatura adresini kişiye aktar

    Kişi kaydı bir firmaya bağlı açıldığında firmanın fatura adresini kişinin posta adresi (Mailing) alanlarına kopyalar; kişi kartında adres boş kalmaz, toplu posta ve fatura yazdırmada eksik adres çıkmaz.

    Workflow · On Create98 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.