Почему не загружается новая номенклатура с тем же артикулом в 1С и как изменить поиск по производителю

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

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

В данной статье мы подробно разберем, почему возникает эта проблема, проанализируем исходный код обработки, рассмотрим несколько вариантов решения (от простого отключения проверки до создания «умного» запроса по артикулу и производителю), а также разберем важные нюансы использования режима загрузки данных и архитектурные альтернативы 1С.

Анализ проблемы и исходного кода обработки

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


Запрос = Новый Запрос;
Артикул = СтрокаТекст.Артикул;
Наименование = СтрокаТекст.Наименование;
Запрос.УстановитьПараметр("Артикул", Артикул);
Запрос.Текст = "ВЫБРАТЬ
               |	Номенклатура.Ссылка
               |ИЗ
               |	Справочник.Номенклатура КАК Номенклатура
               |ГДЕ
               |	Номенклатура.Артикул = &Артикул
               |	И НЕ Номенклатура.ПометкаУдаления";
Выборка = Запрос.Выполнить.Выбрать;

Проанализируем логику этого фрагмента. Если в базе данных находится хотя бы один товар с аналогичным артикулом (даже если у него совершенно другой производитель или наименование), условие Если Выборка.Следующий() тогда возвращает Истина. Программа записывает найденную ссылку в переменную НоменклатураСсылка и завершает выполнение функции, пропуская блок Иначе, в котором должны создаваться новый производитель и новая карточка товара.

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


Вариант решения №1: Полное отключение проверки артикула (Создание дублей)

Самый простой способ, который часто выбирают разработчики для быстрого решения задачи, — это полное комментирование или удаление блока проверки уникальности артикула (как предложено во втором сообщении темы). В этом случае система вообще не будет искать существующие товары, а начнет сразу создавать новые элементы в справочнике Номенклатура.

Рассмотрим, как изменится код функции при таком подходе:


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

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


Вариант решения №2: Умный поиск по Артикулу и Производителю (Рекомендуемый вариант)

Чтобы избежать дублирования и корректно идентифицировать товары, необходимо выполнять поиск по связке «Артикул + Производитель». Однако здесь кроется важная техническая деталь, которую часто упускают начинающие разработчики.

В современных конфигурациях 1С (таких как «Управление торговлей 11», «ERP Управление предприятием» или «Комплексная автоматизация») реквизит Производитель чаще всего не является стандартным реквизитом справочника Номенклатура. Он хранится в табличной части ДополнительныеРеквизиты объекта.

Попытка выполнить простой запрос вида Номенклатура.Производитель = &Производитель вызовет ошибку отсутствия реквизита. Чтобы правильно реализовать поиск, мы должны соединить основную таблицу справочника с табличной частью его дополнительных реквизитов. Разберем по шагам, как это сделать программно.

Ниже представлен корректный код функции с предварительным поиском производителя и последующим поиском номенклатуры по сочетанию артикула и ссылки на производителя:


&НаСервере
Функция ПолучитьНоменклатуру(СтрокаТекст)
    
    Артикул = СтрокаТекст.Артикул;
    Наименование = СтрокаТекст.Наименование;
    РодительСсылка = ГруппаНоменклатуры;
    
    // Шаг 1. Сначала ищем или создаем производителя в базе данных,
    // так как его ссылка понадобится нам для точного поиска номенклатуры.
    ТекстПроизводитель = СокрЛП(СтрокаТекст.Производитель);
    Если ЗначениеЗаполнено(ТекстПроизводитель) Тогда
        ЗапросПроизводитель = Новый Запрос;
        ЗапросПроизводитель.УстановитьПараметр("Наименование", ТекстПроизводитель);
        ЗапросПроизводитель.УстановитьПараметр("Владелец", ХарактеристикаПроизводитель);
        ЗапросПроизводитель.Текст = 
            "ВЫБРАТЬ
            |	ЗначенияСвойствОбъектов.Ссылка КАК Ссылка
            |ИЗ
            |	Справочник.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
            |ГДЕ
            |	ЗначенияСвойствОбъектов.Наименование = &Наименование
            |	И ЗначенияСвойствОбъектов.Владелец = &Владелец";
        
        ВыборкаПроизводитель = ЗапросПроизводитель.Выполнить().Выбрать();
        Если ВыборкаПроизводитель.Следующий() Тогда
            ПроизводительСсылка = ВыборкаПроизводитель.Ссылка;
        Else
            ПроизводительОбъект = Справочники.ЗначенияСвойствОбъектов.СоздатьЭлемент();
            ПроизводительОбъект.Владелец = ХарактеристикаПроизводитель;
            ПроизводительОбъект.Наименование = ТекстПроизводитель;
            Попытка
                ПроизводительОбъект.Записать();
                ПроизводительСсылка = ПроизводительОбъект.Ссылка;
            Исключение
                Сообщить("Ошибка при записи производителя: " + ОписаниеОшибки());
                ПроизводительСсылка = Справочники.ЗначенияСвойствОбъектов.ПустаяСсылка();
            КонецПопытки;
        КонецЕсли;
    Иначе
        ПроизводительСсылка = Справочники.ЗначенияСвойствОбъектов.ПустаяСсылка();
    КонецЕсли;
    
    // Шаг 2. Ищем существующую номенклатуру по сочетанию Артикула и Производителя
    НоменклатураСсылка = Справочники.Номенклатура.ПустаяСсылка();
    
    ЗапросНоменклатура = Новый Запрос;
    ЗапросНоменклатура.УстановитьПараметр("Артикул", Артикул);
    ЗапросНоменклатура.УстановитьПараметр("СвойствоПроизводитель", ХарактеристикаПроизводитель);
    ЗапросНоменклатура.УстановитьПараметр("ПроизводительСсылка", ПроизводительСсылка);
    
    // Используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ для проверки дополнительного реквизита
    ЗапросНоменклатура.Текст = 
        "ВЫБРАТЬ РАЗЛИЧНЫЕ
        |	Номенклатура.Ссылка КАК Ссылка
        |ИЗ
        |	Справочник.Номенклатура КАК Номенклатура
        |	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
        |		ПО НоменклатураДополнительныеРеквизиты.Ссылка = Номенклатура.Ссылка
        |ГДЕ
        |	Номенклатура.Артикул = &Артикул
        |	И НЕ Номенклатура.ПометкаУдаления
        |	И НоменклатураДополнительныеРеквизиты.Свойство = &СвойствоПроизводитель
        |	И НоменклатураДополнительныеРеквизиты.Значение = &ПроизводительСсылка";
        
    ВыборкаНоменклатуры = ЗапросНоменклатура.Run().Select();
    
    Если ВыборкаНоменклатуры.Next() Тогда
        // Товар с таким артикулом и производителем уже есть в базе
        НоменклатураСсылка = ВыборкаНоменклатуры.Ссылка;
    Иначе
        // Шаг 3. Создаем новую карточку товара, если поиск не дал результатов
        НоменклатураОбъект = Справочники.Номенклатура.СоздатьЭлемент();
        НоменклатураОбъект.ТипНоменклатуры = Перечисления.ТипыНоменклатуры.Товар;
        НоменклатураОбъект.ВидНоменклатуры = ВидНоменклатуры;
        НоменклатураОбъект.Качество = Перечисления.ГрадацииКачества.Новый;
        НоменклатураОбъект.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС;
        
        ЗаполнитьЗначенияСвойств(НоменклатураОбъект, ВидНоменклатуры, , "Ссылка, Владелец, ПометкаУдаления, ВерсияДанных, Предопределенный, ЭтоГруппа, Родитель, Описание, Наименование, Код, ИмяПредопределенныхДанных");
        
        НоменклатураОбъект.Наименование = Наименование;
        НоменклатураОбъект.НаименованиеПолное = Наименование;
        НоменклатураОбъект.Артикул = Артикул;
        НоменклатураОбъект.Родитель = РодительСсылка;
        
        // Заполняем дополнительный реквизит производителя
        Если ЗначениеЗаполнено(ПроизводительСсылка) Тогда
            ЗаписьПроизводитель = НоменклатураОбъект.ДополнительныеРеквизиты.Добавить();
            ЗаписьПроизводитель.Значение = ПроизводительСсылка;
            ЗаписьПроизводитель.Свойство = ХарактеристикаПроизводитель;
        КонецЕсли;
        
        Попытка
            НоменклатураОбъект.Записать();
            НоменклатураСсылка = НоменклатураОбъект.Ссылка;
        Исключение
            Сообщить("Ошибка при записи номенклатуры: " + ОписаниеОшибки());
        КонецПопытки;
    КонецЕсли;
    
    Возврат НоменклатураСсылка;
КонецФункции

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


Использование свойства ОбменДанными.Загрузка = Истина

В ходе обсуждения на форумах часто советуют использовать конструкцию ОбменДанными.Загрузка = Истина. Выясним подробно, для чего она нужна, куда ее помещать в коде и какие опасности она в себе таит.

Эта директива устанавливается непосредственно для создаваемого объекта перед выполнением метода Записать(). Рассмотрим пример внедрения этой строки:


НоменклатураОбъект = Справочники.Номенклатура.СоздатьЭлемент();
// ... [Заполнение реквизитов объекта] ...

// Устанавливаем признак загрузки данных
НоменклатураОбъект.ОбменДанными.Загрузка = Истина;

Попытка
    НоменклатураОбъект.Записать();
Исключение
    Сообщить(ОписаниеОшибки());
КонецПопытки;

Что дает этот флаг:

  1. Отключение проверок: Платформа полностью отключает прикладные проверки конфигурации, расположенные в обработчиках событий ПередЗаписью и ПриЗаписи (как в модуле объекта, так и в подписках на события). Если в вашей системе настроен программный запрет на создание дублирующихся артикулов, этот флаг позволит обойти его.
  2. Ускорение записи: Объект записывается в базу данных значительно быстрее, так как не тратится время на выполнение тяжелых бизнес-правил, формирование связанных наименований и генерацию сопутствующих данных.
  3. Отключение регистрации для обменов: Этот элемент не будет автоматически зарегистрирован на планах обмена для выгрузки в другие связанные базы (например, в бухгалтерию), что полезно при массовом первоначальном импорте.

В чем заключаются риски (предупреждение из темы форума):

Обход стандартных механизмов проверки может нарушить логическую целостность вашей базы данных. При установленном флаге Загрузка = Истина система может не рассчитать автоматически хэш-коды для быстрого поиска, не заполнить обязательные скрытые реквизиты или пропустить генерацию рабочих наименований по шаблонам вида номенклатуры. В будущем это может привести к непредсказуемым ошибкам, например, когда менеджеры не смогут провести документ реализации с этой номенклатурой из-за незаполненных скрытых полей или некорректных признаков учета (учет по характеристикам, сериям и т.д.).


Настройка контроля уникальности в режиме пользователя

Прежде чем вносить изменения в программный код обработки или конфигурации, проанализируем возможности стандартного функционала 1С. В типовых решениях (УТ, ERP, КА) проверка уникальности артикулов зачастую вынесена в пользовательские настройки.

Для каждого Вида номенклатуры можно гибко настроить контроль заполнения и уникальности реквизитов. Если в настройках конкретного вида номенклатуры включен «Контроль уникальности по сочетанию реквизитов (например, артикул + производитель)», платформа самостоятельно будет следить за уникальностью при записи и не выдаст ошибку, если артикулы совпадают у разных производителей. Обязательно проверьте эти настройки в интерфейсе вашей программы перед доработкой кода.


Альтернативный архитектурный подход: Номенклатура контрагентов

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

Если товары по своим физическим характеристикам и назначению идентичны, а различия заключаются лишь в наименованиях и артикулах у разных поставщиков, в 1С предусмотрен встроенный и гораздо более эффективный механизм — справочник «Номенклатура контрагентов» (в некоторых конфигурациях — «Номенклатура партнеров»):

Использование этого механизма позволяет сохранить базу данных в чистоте, избежать дублирования позиций на складах и одновременно с этим корректно обрабатывать различные артикулы внешних поставщиков при импорте.

← На главную