Как распределить и разбить строки заказа по остаткам на складах в 1С?

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

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

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

Принцип работы алгоритма распределения

Проанализируем задачу на простом примере. Предположим, клиент заказал 5 штук определенной номенклатуры. По данным регистра остатков у нас есть:

  1. Основной склад: остаток 2 шт.
  2. Дополнительный склад: остаток 10 шт.

Нам необходимо получить в заказе две строки: первую на 2 шт. с указанием Основного склада, и вторую на 3 шт. с указанием Дополнительного склада.

Для решения этой задачи используются две таблицы:

  1. Таблица потребностей: строки табличной части документа с колонками Номенклатура и Количество.
  2. Таблица остатков: результат запроса к регистру накопления с колонками Номенклатура, Склад и Количество (отсортированная по приоритету складов).

Базовая программная реализация

Рассмотрим классический вариант алгоритма с обходом строк табличной части и поиском подходящих остатков. Для вычисления списываемого объема используется встроенная функция Мин().


// ТаблицаОстатков получена запросом к регистру накопления
// и содержит колонки: Номенклатура, Склад, Количество

ТаблицаРезультат = СсылкаНаЗаказ.Товары.Выгрузить();
ТаблицаРезультат.Очистить();

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

СсылкаНаЗаказ.Товары.Загрузить(ТаблицаРезультат);

Подводные камни и важные доработки

При внедрении данного алгоритма в реальную конфигурацию следует обратить внимание на несколько критических нюансов:

  1. Уменьшение остатка в рабочей таблице: Если в документе одна и та же номенклатура встречается в нескольких строках (например, с разными характеристиками или ценами), обязательно нужно уменьшать СтрокаОстатка.Количество сразу после размещения, чтобы один и тот же свободный остаток не был распределен повторно.
  2. Обработка нехватки (дефицита): Если суммарного остатка по всем складам недостаточно, переменная ОсталосьРазместить останется больше нуля. В таком случае необходимо добавить строку с остатком непокрытого количества с пустым складом или специальным статусом «К обеспечению».
  3. Пересчет суммовых колонок: При простом дроблении количества суммы в строках могут стать некорректными. После создания новой строки необходимо пересчитать суммы: Сумма = Количество * Цена, а также скорректировать суммы НДС и скидок (либо вызвать штатную процедуру пересчета строки табличной части).
  4. Потеря ключей связи: Метод полной перезагрузки через Выгрузить() и Загрузить() сбрасывает уникальные идентификаторы строк (например, КлючСтроки или КлючСвязи). Если в конфигурации к строкам товаров привязаны подчиненные табличные части (акцизные марки, серийные номера, этапы графиков), лучше модифицировать строки напрямую в табличной части, генерируя новые уникальные ключи для отщепляемых позиций.

Где выполнять распределение: при проведении или по кнопке?

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

  1. Интерактивное заполнение по кнопке на форме: Менеджер вводит исходный список товаров, нажимает команду «Заполнить склады» или «Распределить по остаткам», после чего табличная часть переформировывается. Менеджер видит результат, может скорректировать распределение вручную и провести документ. Это исключает неожиданные изменения в документе задним числом.
  2. Автоматическое распределение при проведении: Если модифицировать табличную часть прямо в обработке проведения, то при повторном проведении документа (например, при восстановлении последовательности или закрытии месяца) строки могут перераспределиться на другие склады из-за изменившейся картины остатков. Поэтому при проведении рекомендуется формировать движения по нужным складам в регистрах накопления, не изменяя исходную табличную часть документа.
← На главную