Как предотвратить дублирование строк в списке заказов клиента и ограничить создание нескольких заказов на производство?

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Промышленность, строительство и АПК
← На главную

В процессе кастомизации типовых решений, таких как 1С:ERP Управление предприятием 2.4, разработчики часто сталкиваются с задачей вывода информации о подчиненных документах непосредственно в форму списка родительского документа. Типичный пример — отображение номера Заказа на производство в списке Заказов клиентов. Однако при стандартном подходе через добавление колонки в расширении возникает проблема: если пользователь создает более одного подчиненного документа, строка родительского заказа в списке начинает дублироваться. Рассмотрим, как решить эту проблему программно и на уровне проектирования интерфейса.

Анализ причины проблемы: почему дублируются строки?

Прежде чем переходить к коду, разберем механику работы динамического списка. Когда мы добавляем в запрос динамического списка ЛЕВОЕ СОЕДИНЕНИЕ с таблицей подчиненных документов, СУБД возвращает столько строк, сколько найдено соответствий. Если на один Заказ клиента приходится два Заказа на производство, в результате выполнения запроса мы получим две идентичные строки заказа клиента, различающиеся только номером производства. Это нарушает целостность восприятия данных пользователем и затрудняет работу с формой.

Проанализируем ситуацию: автор темы хочет внедрить жесткую связь «один к одному», где одному клиентскому заказу соответствует строго одно производство. Для реализации этой задачи нам потребуется комплексный подход: запрет на создание лишних документов и корректная настройка отображения данных.

Решение 1: Программный запрет на создание второго документа

Наиболее логичным местом для проверки уникальности связи является модуль объекта документа Заказ на производство. Мы воспользуемся обработчиком события ПередЗаписью. Выясним, почему именно этот метод предпочтительнее. Использование события ПриСозданииНаСервере в форме не дает гарантии, так как ДокументОснование может быть изменен пользователем уже после открытия формы. Проверка непосредственно в модуле объекта сработает всегда, независимо от того, создается ли документ вручную или программно.

Рассмотрим пошагово алгоритм проверки в модуле объекта ЗаказНаПроизводство:

  1. Проверяем, не выполняется ли загрузка данных (обмен), чтобы не блокировать импорт.
  2. Создаем запрос к базе данных для поиска существующих активных заказов на производство с таким же основанием.
  3. Исключаем из поиска текущую ссылку, чтобы документ мог перезаписывать сам себя.
  4. Исключаем помеченные на удаление документы, так как они не должны блокировать создание новых корректных заказов.
  5. Если запрос находит записи — отменяем запись и выводим информационное сообщение.

Разберем пример кода, который можно внедрить через расширение в модуль объекта:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Если идет загрузка данных, проверку пропускаем
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Проверяем наличие заполненного основания
    Если ЗначениеЗаполнено(ДокументОснование) Тогда
        
        лкЗапрос = Новый Запрос;
        лкЗапрос.Текст = 
            "ВЫБРАТЬ
            |	Док.Ссылка КАК Ссылка
            |ИЗ
            |	Документ.ЗаказНаПроизводство КАК Док
            |ГДЕ
            |	Док.ДокументОснование = &Основание
            |	И Док.Ссылка <> &Ссылка
            |	И НЕ Док.ПометкаУдаления";
            
        лкЗапрос.УстановитьПараметр("Основание", ДокументОснование);
        лкЗапрос.УстановитьПараметр("Ссылка", Ссылка);
        
        РезультатЗапроса = лкЗапрос.Выполнить();
        
        Если НЕ РезультатЗапроса.Пустой() Тогда
            лкВыборка = РезультатЗапроса.Выбрать();
            Пока лкВыборка.Следующий() Цикл
                Сообщить("Ошибка: Для заказа клиента " + ДокументОснование + " уже существует активный " + лкВыборка.Ссылка);
            КонецЦикла;
            
            // Устанавливаем Отказ в Истина, чтобы прервать запись
            Отказ = Истина;
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

Решение 2: Корректная настройка динамического списка

Даже при наличии проверки ПередЗаписью, в базе могут оказаться «дубли» (например, созданные до внедрения проверки или загруженные программно без проверок). Чтобы форма списка Заказ клиента не «разлеталась» на несколько строк, проанализируем, как правильно написать запрос динамического списка.

Вместо прямого левого соединения с таблицей заказов на производство, выполним группировку по основанию. Это позволит нам всегда получать одну строку для одного клиентского заказа. Посмотрим на пример структуры такого запроса:


ВЫБРАТЬ
    ЗаказКлиента.Ссылка,
    ЗаказКлиента.Номер,
    ЗаказКлиента.Дата,
    ПодзапросПроизводство.НомерЗаказаНаПроизводство
ИЗ
    Документ.ЗаказКлиента КАК ЗаказКлиента
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            ДокПроизв.ДокументОснование КАК ДокументОснование,
            МАКСИМУМ(ДокПроизв.Номер) КАК НомерЗаказаНаПроизводство
        ИЗ
            Документ.ЗаказНаПроизводство КАК ДокПроизв
        ГДЕ
            НЕ ДокПроизв.ПометкаУдаления
        СГРУППИРОВАТЬ ПО
            ДокПроизв.ДокументОснование
    ) КАК ПодзапросПроизводство
    ПО ЗаказКлиента.Ссылка = ПодзапросПроизводство.ДокументОснование

Использование агрегатной функции МАКСИМУМ (или МИНИМУМ) гарантирует, что для связи будет выбрано только одно значение номера, даже если документов в базе несколько. Это полностью исключает дублирование строк в интерфейсе.

Решение 3: Гибкий вывод через запятую (для сложных случаев)

Если бизнес-логика предприятия изменится и возникнет необходимость создавать несколько производственных заказов на один клиентский (например, для разных цехов), то жесткий запрет станет помехой. В таком случае рассмотрим более гибкий вариант отображения — вывод всех номеров в одну ячейку через запятую.

Для этого в динамическом списке можно использовать вычисляемые поля или программную обработку события ПриПолученииДанныхНаСервере. Однако наиболее современным методом для 1С:ERP является использование механизмов системы компоновки данных (СКД), встроенных в динамический список. Мы можем использовать функцию Массив() в ресурсах СКД (в последних версиях платформы), чтобы собрать все связанные номера, и затем применить функцию СоединитьСтроки().

Важные методологические замечания

Проанализируем риски внедрения жесткого ограничения «1 заказ клиента = 1 заказ на производство» в ERP 2.4:

Таким образом, мы разобрали комплексное решение: от программной блокировки избыточных данных до корректного построения SQL-запросов в динамических списках. Выбор конкретного метода зависит от жесткости требований бизнес-процесса на вашем предприятии.

← На главную