Как реализовать автоматическое распределение серий номенклатуры по остаткам в 1С:Управление торговлей 11.4

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

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

Постановка задачи и основные сложности

Представим ситуацию: мы читаем файл Excel, в котором указан артикул товара и количество. В 1С этот товар может храниться на складе в разрезе разных серий (например, с разными сроками годности). Нам нужно не просто создать строку в документе Заказ клиента, а реализовать логику FEFO (First Expired, First Out) или простого списания доступных остатков. Если заказано 10 единиц, а на первой найденной серии всего 4, программа должна автоматически добавить вторую строку с другой серией на оставшиеся 6 единиц.

Шаг 1. Поиск номенклатуры: оптимизация запроса

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

Важный совет: избегайте конструкции ПОДОБНО, если артикул известен точно. Это избавит вас от получения «лишних» позиций номенклатуры в выборке.


// Пример быстрого поиска номенклатуры
НоменклатураСсылка = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул", АртикулИзФайла);
Если НоменклатураСсылка.Пустая() Тогда
    Сообщить("Товар с артикулом " + АртикулИзФайла + " не найден!");
    Продолжить;
КонецЕсли;

Шаг 2. Получение остатков по сериям

Для распределения нам необходимо знать, какие серии есть в наличии на конкретном складе. Проанализируем ситуацию: использовать виртуальную таблицу ОстаткиИОбороты излишне, если нам нужны только текущие данные. Правильнее использовать таблицу Остатки регистра накопления ТоварыНаСкладах.

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


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

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

ВыборкаСерий = ЗапросСерии.Выполнить().Выбрать();

Шаг 3. Алгоритм распределения количества

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

  1. Создаем переменную-счетчик ОсталосьРаспределить.
  2. Входим в цикл по доступным сериям.
  3. Если остатка серии больше или равно, чем нужно распределить — заполняем строку и завершаем распределение.
  4. Если остатка меньше — забираем весь остаток серии, уменьшаем счетчик и переходим к следующей серии.

ОсталосьРаспределить = КоличествоИзФайла;

Пока ВыборкаСерий.Следующий() И ОсталосьРаспределить > 0 Цикл
    
    НоваяСтрока = ОбъектЗаказ.Товары.Добавить();
    НоваяСтрока.Номенклатура = НоменклатураСсылка;
    НоваяСтрока.Серия = ВыборкаСерий.Серия;
    
    // Определяем, сколько мы можем взять из текущей серии
    ВзятьИзСерии = Мин(ОсталосьРаспределить, ВыборкаСерий.Остаток);
    
    НоваяСтрока.Количество = ВзятьИзСерии;
    НоваяСтрока.КоличествоУпаковок = ВзятьИзСерии;
    
    // Заполняем служебные поля УТ 11
    НоваяСтрока.СтатусУказанияСерий = 0; // Значение зависит от настроек политики серий
    НоваяСтрока.ВариантОбеспечения = Перечисления.ВариантыОбеспечения.Отгрузить;
    
    // Уменьшаем остаток для следующей итерации
    ОсталосьРаспределить = ОсталосьРаспределить - ВзятьИзСерии;
    
КонецЦикла;

Если ОсталосьРаспределить > 0 Тогда
    Сообщить("Внимание: По товару " + НоменклатураСсылка + " не хватило остатка: " + ОсталосьРаспределить);
КонецЕсли;

Шаг 4. Особенности работы с полем «СтатусУказанияСерий»

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

Важный момент: если в вашей политике учета серии указываются при планировании отгрузки, то СтатусУказанияСерий может принимать значение, равное количеству в строке. Если же серии заполняются в отдельном окне, программная подстановка может потребовать вызова типовых процедур пересчета из модулей конфигурации.

Шаг 5. Работа с Excel без COM-объекта

В исходном обсуждении упоминался COMОбъект("Excel.Application"). Рассмотрим альтернативу. Использование COM требует установленного Excel на сервере, что часто невозможно. Мы рекомендуем использовать встроенные средства 1С для чтения файлов .xlsx через ТабличныйДокумент.


ТабДок = Новый ТабличныйДокумент;
Попытка
    ТабДок.Прочитать(ИмяФайла, СпособЧтенияЗначенийТабличногоДокумента.Значение);
Исключение
    Сообщить("Ошибка чтения файла: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

ВсегоСтрок = ТабДок.ВысотаТаблицы;
Для Стр = 2 По ВсегоСтрок Цикл
    Артикул = ТабДок.Область(Стр, КолонкаАртикул).Текст;
    Количество = Число(ТабДок.Область(Стр, КолонкаКоличество).Текст);
    // ... дальнейшая логика поиска и распределения
КонецЦикла;

Рекомендации по оптимизации

Разберем, как сделать загрузку еще быстрее, если в файле тысячи строк:

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

← На главную