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