Как сопоставить номенклатуру и избежать дублей при загрузке в накладную

Программист Управленческий учет Торговля и дистрибуция
← На главную

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

Проблема запросов внутри цикла

Рассмотрим типичную ошибку начинающих разработчиков: выполнение запроса к базе данных внутри цикла перебора строк. Проанализируем ситуацию: если в вашем Excel-файле 1000 строк, программа выполнит 1000 отдельных обращений к серверу БД. Это крайне неэффективно и значительно замедляет работу системы. Профессиональный подход заключается в том, чтобы получить все необходимые данные одним запросом до начала цикла или использовать встроенные коллекции объектов для быстрого поиска.

Способ 1: Использование метода Найти() коллекции строк

Если производительность не является критическим фактором (например, в документе не более пары сотен строк), самым простым и понятным решением будет использование метода Найти() табличной части документа. Рассмотрим, как это реализовать программно:


// Предположим, Накладная — это ссылка на документ
ДокОбъект = Накладная.ПолучитьОбъект();

Для Каждого СтрокаИзExcel Из Товары Цикл
    Если ЗначениеЗаполнено(СтрокаИзExcel.Номенклатура) Тогда
        // Ищем номенклатуру в табличной части "Товары" документа
        НайденнаяСтрока = ДокОбъект.Товары.Найти(СтрокаИзExcel.Номенклатура, "Номенклатура");
        
        Если НайденнаяСтрока = Неопределено Тогда
            // Если товар не найден, добавляем новую строку
            НоваяСтрока = ДокОбъект.Товары.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИзExcel);
            // Дозаполняем специфические реквизиты
            НоваяСтрока.ЕдиницаИзмерения = СтрокаИзExcel.Номенклатура.БазоваяЕдиницаИзмерения;
        Иначе
            // Товар уже есть, можно пропустить или обновить количество
            Сообщить("Товар " + СтрокаИзExcel.Номенклатура + " уже есть в документе. Пропускаем.");
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

ДокОбъект.Записать();

Этот метод хорош тем, что не требует написания сложных текстов запросов и легко отлаживается. Мы используем ЗаполнитьЗначенияСвойств для быстрого копирования данных между строками, если имена колонок в вашей таблице и в документе совпадают.

Способ 2: Использование запроса с оператором В (&Список)

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


// Сначала соберем весь список номенклатуры из загрузки
СписокНоменклатуры = Товары.ВыгрузитьКолонку("Номенклатура");

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

Запрос.УстановитьПараметр("Ссылка", Накладная);
Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатуры);

// Получаем список товаров, которые уже есть в документе
РезультатЗапроса = Запрос.Выполнить().Выгрузить();
СписокСуществующих = РезультатЗапроса.ВыгрузитьКолонку("Номенклатура");

Для Каждого СтрокаИзExcel Из Товары Цикл
    // Проверяем, есть ли текущая номенклатура в списке существующих
    Если СписокСуществующих.Найти(СтрокаИзExcel.Номенклатура) = Неопределено Тогда
        // Добавляем, так как товара еще нет в накладной
        НоваяСтрока = ДокОбъект.Товары.Добавить();
        // ... заполнение реквизитов ...
    КонецЕсли;
КонецЦикла;

Способ 3: Оптимизация через Временные Таблицы (ВТ)

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


Запрос = Новый Запрос;
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Помещаем таблицу товаров из Excel в запрос
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВнешниеТовары.Номенклатура,
    |	ВнешниеТовары.Количество,
    |	ВнешниеТовары.Цена
    |ПОМЕСТИТЬ ВТ_Загрузка
    |ИЗ
    |	&ТаблицаТоваров КАК ВнешниеТовары";

Запрос.УстановитьПараметр("ТаблицаТоваров", Товары);
Запрос.Выполнить();

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

Запрос.УстановитьПараметр("Ссылка", Накладная);
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    НовСтр = ДокОбъект.Товары.Добавить();
    ЗаполнитьЗначенияСвойств(НовСтр, Выборка);
КонецЦикла;

Использование ЛЕВОЕ СОЕДИНЕНИЕ с проверкой на NULL — это классический и самый быстрый способ в SQL и 1С найти отсутствующие записи в одной таблице относительно другой.

Дополнительные рекомендации по работе в Далион и других системах

Работа с обычными формами и конфигурациями типа "Далион" накладывает свои особенности. Посмотрим на важные нюансы, которые помогут сделать вашу обработку профессиональной:

  1. Поиск не только по ссылке: Часто в Excel нет внутренних идентификаторов 1С. В таких случаях сопоставление следует делать по Артикулу или Штрихкоду. Если вы ищете по штрихкоду, помните, что это может потребовать обращения к регистру сведений Штрихкоды.
  2. Обновление количества: Иногда бизнес-логика требует не "пропускать" существующий товар, а прибавлять количество из файла к уже существующему в документе. В этом случае вместо Добавить() используйте найденную строку: НайденнаяСтрока.Количество = НайденнаяСтрока.Количество + СтрокаИзExcel.Количество;.
  3. Использование транзакций: При записи больших документов всегда используйте конструкцию НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Это обезопасит данные, если в процессе записи произойдет сбой — система просто откатит изменения к исходному состоянию.
  4. Работа с формой: Если вы хотите, чтобы пользователь сразу увидел результат, после записи документа можно открыть его форму методом ОткрытьФорму() или обновить текущую, если код выполняется в контексте формы.

Подводя итог, можно сказать, что выбор метода зависит от объема данных. Для небольших накладных идеально подходит метод Найти(), а для массовой загрузки тысяч позиций — временные таблицы в запросах. Разберем эти примеры на практике, и вы увидите, насколько стабильнее станет работать ваша загрузка данных.

← На главную