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ığı
- 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_v8olmalı (koddakiconnection:ile birebir); oluşturduktan sonra açılan sayfada kontrol edin. ScopeZohoCRM.modules.ALLve Zoho'nun arama API'sinin istediğiZohoSearch.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. - 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.
- 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 automationimzasıyla bağlanamaz. - 04
Hangi kuralı kurarsanız kurun argüman
leadId, Lead Id alanına eşlenir. - 05
Veri merkezi: URL'ler
zohoapis.eu; org ABD'deysezohoapis.com, Hindistan'daysazohoapis.inyazılır.
Alan eşlemesi
Koda yazılan alanlar ve kendi org'unuzda dikkat etmeniz gerekenler.
| Alan | Değer | Not |
|---|---|---|
| Phone, Mobile | okunur | Aramada 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ır | Master'ın kendisi ve tekrarlar elenir. |
| _fields | 17 standart alan | Salutation, 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 API | POST /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)
{
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();
}
}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,
SCHEDULEDdö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
- 05Leads · 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.
- 06Calls · 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.
- 07Leads
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.