Исключение дублирования контрагентов

Часто бывает, что необходимо обеспечить уникальность контрагентов в базе данных. В системе по умолчанию не организовано ограничение при вводе юридических и физических лиц. Реальная компания имеет свои правила при добавлении контрагента.

Рассмотрим популярную ситуацию, когда правило уникальности для контрагента формулируется так: в базе данных не может быть контрагентов, у которых одинаковые ИНН и город в юридическом адресе.

Реализовать это можно через перехват событий на обновление в базе данных.

Внимание!
Некорректное применение приведенного в данной статье сценария может привести к блокировке всех транзакций в БД. Убедитесь, что ваш код работает только для требуемых условий, во всех остальных случаях должна выполняться строка return false; // возврат в норме.

Алгоритм следующий. Перед тем, как изменения в объекте будут записаны в базу данных (метод OnPreUpdate), определим тип объекта (IContractor). Если у контрагента указан город в юридическом адресе, ищем с таким же ИНН и городом среди сущаствующих. Если находим, выводим сообщение о не уникальности контрагента. Если город не указан, ищем с таким же ИНН.

Пример сценария с использованием PublicAPI

Примечание
Актуальная документация по PublicAPI доступна по ссылке.
Внимание!
Сценарий, указанный ниже, актуален только для версий системы ELMA до 3.12.1 включительно.

Для корректной работы сценария необходимо подключить следующие пространства имен:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using EleWise.ELMA.API;
using EleWise.ELMA.ComponentModel;
using EleWise.ELMA.CRM.Models;
using EleWise.ELMA.Logging;
using EleWise.ELMA.Runtime.NH.Listeners;
using EleWise.ELMA.Runtime.Settings;
using EleWise.ELMA.Services;
using NHibernate.Event;

Текст сценария:

[Component] 
        public class ReSaveContractor : EntityEventsListener
        {
            public override bool OnPreUpdate(PreUpdateEvent @event)
            {  
                // Определяем тип объекта 
                //---------------------------------------------------------- 
                if (@event.Entity is IContractor) // интерфейс объекта типа Контрагент 
                { 
                    var Kontragent = (IContractor)@event.Entity; 
           
                    if (Kontragent.LegalAddress != null && !string.IsNullOrWhiteSpace(Kontragent.LegalAddress.City)) // Если ввели город в юридическом адресе 
                    { 
                        // Ищем по фильтру контрагента указываем ИНН и город и исключаем текущего контрагента 
                        var findKA = PublicAPI.CRM.Contractor.Find(string.Format(@"(INN is NULL OR INN = ’{0}’) and LegalAddress in (City LIKE ’%{1}%’) and Id <> {2}", Kontragent.INN, Kontragent.LegalAddress.City.Trim(), Kontragent.Id));
                        if (findKA.Count == 0)
                        { 
                            Logger.Log.Error("==Выход нет дублей "); 
                        } else
                        {
                            throw new Exception("Контрагент уже существует. " + string.Format(@"{0}/CRM/ContractorLegal/Details/{1}", 
                                Locator.GetServiceNotNull<CommonSettingsModule>().Settings.ApplicationBaseUrl, 
                                findKA.ElementAt(0).Id) +
                            " Укажите другие ИНН и город."); 
                        } 
                    } else
                    { 
                        // Ищем по фильтру контрагента указываем ИНН и город и исключаем текущего контрагента 
                        var findKA = PublicAPI.CRM.Contractor.Find(string.Format(@"(INN is NULL OR INN = ’{0}’) and LegalAddress in (City is NULL OR City = ’’) and Id <> {1}", Kontragent.INN, Kontragent.Id));
                        if (findKA.Count == 0)
                        { 
                            Logger.Log.Error("==Выход нет дублей "); 
                        } else
                        {
                            throw new Exception("Контрагент уже ." + string.Format(@"{0}/CRM/ContractorLegal/Details/{1}", 
                                Locator.GetServiceNotNull<CommonSettingsModule>().Settings.ApplicationBaseUrl, 
                                findKA.ElementAt(0).Id) +
                            " Укажите другие ИНН и город."); 
                        }      
                    }     
                } 
                return false; // возврат в норме 
            }
        }

Пример сценария без использования PublicAPI

Для этого создадим специальный объект в Дизайнере и назовём его Перехватчик. На вкладке Сценарии карточки этого объекта подключим необходимые пространства в начале сценария:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using EleWise.ELMA.ComponentModel;
using EleWise.ELMA.CRM.Models;
using EleWise.ELMA.Logging;
using EleWise.ELMA.Model.Managers;
using EleWise.ELMA.Runtime.NH.Listeners;
using EleWise.ELMA.Runtime.Settings;
using EleWise.ELMA.Services;
using NHibernate.Event;
 Текст сценария:
[Component] 
public class ReSaveContractor : EntityEventsListener 
{ 
	public override bool OnPreUpdate(PreUpdateEvent @event) 
	{  
		// Определяем тип объекта 
		//---------------------------------------------------------- 
		if(@event.Entity is IContractor) // интерфейс объекта типа Контрагент 
		{ 
			var Kontragent = (IContractor)@event.Entity; 
		  
			if (Kontragent.LegalAddress != null && !string.IsNullOrWhiteSpace(Kontragent.LegalAddress.City)) // Если ввели город в юридическом адресе 
			{ 
				// Ищем по фильтру контрагента указываем ИНН и город и исключаем текущего контрагента 
				var filterString = string.Format(@"(INN is NULL OR INN = ’{0}’) and LegalAddress in (City LIKE ’%{1}%’) and Id <> {2}",Kontragent.INN, Kontragent.LegalAddress.City.Trim(), Kontragent.Id); 
				var findKA = EntityManager<IContractor>.Instance.Find(filterString);      
				if (findKA.Count == 0) 
				{ 
					Logger.Log.Error("==Выход нет дублей "); 
				} 
				else
				{
					throw new Exception("Контрагент уже существует. "+string.Format(@"{0}/CRM/ContractorLegal/Details/{1}", 
					Locator.GetServiceNotNull<CommonSettingsModule>().Settings.ApplicationBaseUrl, 
					findKA.ElementAt(0).Id)+ 
					" Укажите другие ИНН и город."); 
				} 
			} 
			else
			{ 
				// Ищем по фильтру контрагента указываем ИНН и город и исключаем текущего контрагента 
				var filterString = string.Format(@"(INN is NULL OR INN = ’{0}’) and LegalAddress in (City is NULL OR City = ’’) and Id <> {1}",Kontragent.INN, Kontragent.Id); 
				var findKA = EntityManager<IContractor>.Instance.Find(filterString);      
				if (findKA.Count == 0) 
				{ 
					Logger.Log.Error("==Выход нет дублей "); 
				} 
				else
				{
					throw new Exception("Контрагент уже ."+string.Format(@"{0}/CRM/ContractorLegal/Details/{1}", 
					Locator.GetServiceNotNull<CommonSettingsModule>().Settings.ApplicationBaseUrl, 
					findKA.ElementAt(0).Id)+ 
					" Укажите другие ИНН и город."); 
				}      
			}     
		} 
		return false; // возврат в норме 
	} 
}

Здесь можно ещё добавить, что метод OnPreUpdate(PreUpdateEvent @event) вызывается и при создании нового объекта, и при изменении существующего. Таким образом, любые изменения названия и/или города юридического адреса контрагентов будут отслеживаться системой.

Примечание
При поиске существующих контрагентов необходимо исключать текущего из поиска даже при добавлении нового, так как поиск идет с учетом кэша на сервере.