Here's one possible scenario for your code. It doesn't catch misspellings, aliases, and other common data entry problems, but it is case insensitive.
trigger BlockDupeLeadConvert on Lead (after update) {
class UniqueKey {
UniqueKey(string Firstname, String lastName, String email) {
this.firstname = firstname;
this.lastname = lastname;
this.email = email;
}
string FirstName, LastName, Email;
integer hashCode() {
return toString().hashCode();
}
boolean equals(Object o) {
return toString() == ((UniqueKey)o).toString();
}
public override string toString() {
return String.format(
'FirstName = \'\'{0}\'\' AND LastName = \'\'{1}\'\' AND Email = \'\'{2}\'\'',
new String[] {
FirstName==null?'':String.escapeSingleQuotes(FirstName.tolowercase()),
LastName==null?'':String.escapeSingleQuotes(LastName.tolowercase()),
Email==null?'':String.escapeSingleQuotes(Email.tolowercase())
}
);
}
}
Map<UniqueKey, Lead> leads = new Map<UniqueKey, Lead>();
for(Lead record:Trigger.new) {
if(record.isconverted) {
UniqueKey key = new UniqueKey(record.firstname, record.lastname, record.email);
if(leads.containskey(key)) {
record.addError('Duplicate lead conversion.');
} else {
leads.put(key, record);
}
}
}
String[] keys = new String[0];
for(UniqueKey key:leads.keyset()) {
keys.add(key.tostring());
}
String query = string.format(
'SELECT Id,FirstName,LastName,Email FROM Contact WHERE {0}',
new String[] {
String.join(keys, ' OR ')
}
);
if(!leads.isempty()) {
for(Contact record:Database.query(query)) {
UniqueKey key = new UniqueKey(record.firstname, record.lastname, record.email);
if(leads.containskey(key)) {
leads.get(key).addError('Duplicate lead conversion.');
}
}
}
}