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

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

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

Проанализируем базовый пример:

  1. Комплект состоит из: Товар 1 — 10 шт., Товар 2 — 8 шт., Товар 3 — 8 шт.
  2. Если в заказе указано: Товар 1 — 11 шт., Товар 2 — 7 шт., Товар 3 — 9 шт., то собрать можно 0 комплектов (не хватает Товара 2).
  3. Если в заказе: Товар 1 — 21 шт., Товар 2 — 16 шт., Товар 3 — 18 шт., то число полных комплектов равно 2 (определяется минимальной обеспеченностью по Товару 2: 16 / 8 = 2).

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

Алгоритм расчета и ключевые ограничения запросов 1С

Количество собранных комплектов конкретного вида определяется как минимум целых частей от деления доступного количества каждого товара на требуемое количество этого товара по спецификации комплекта.

При написании запроса в 1С важно учитывать следующие особенности:

  1. Особенности функции ВЫРАЗИТЬ: конструкция ВЫРАЗИТЬ(Число КАК ЧИСЛО(10, 0)) выполняет математическое или банковское округление, а не отсечение дробной части (Floor). Если просто поделить 1.9 и выразить как целое число, результат будет равен 2, что приведет к ложному комплекту. Функция Цел() в языке запросов 1С отсутствует.
  2. Дублирование строк в документе: одна и та же позиция номенклатуры может встречаться в документе несколько раз. Табличную часть заказа перед расчетом необходимо обязательно группировать с суммированием количества.
  3. Направление соединения: соединение обязательно должно быть левым от таблицы состава комплекта к таблице документа. Если какого-то обязательного компонента комплекта в документе нет, поле количества должно получить значение 0 через функцию ЕСТЬNULL.

Вариант 1. Оптимальный расчет (запрос + функция Цел во встроенном языке)

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ТЧЗаказа.Номенклатура КАК Номенклатура,
|	СУММА(ТЧЗаказа.Количество) КАК Количество
|ПОМЕСТИТЬ ВТ_ТоварыДокумента
|ИЗ
|	Документ.ЗаказПокупателя.Товары КАК ТЧЗаказа
|ГДЕ
|	ТЧЗаказа.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
|	ТЧЗаказа.Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	СоставКомплекта.Ссылка КАК Комплект,
|	МИНИМУМ(ЕСТЬNULL(ВТ_ТоварыДокумента.Количество, 0) / СоставКомплекта.Количество) КАК КоэффициентОбеспеченности
|ИЗ
|	Справочник.рбхКоллекции.Товары КАК СоставКомплекта
|	ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТоварыДокумента КАК ВТ_ТоварыДокумента
|		ПО СоставКомплекта.Номенклатура = ВТ_ТоварыДокумента.Номенклатура
|ГДЕ
|	СоставКомплекта.Количество > 0
|СГРУППИРОВАТЬ ПО
|	СоставКомплекта.Ссылка
|ИМЕЮЩИЕ
|	МИНИМУМ(ЕСТЬNULL(ВТ_ТоварыДокумента.Количество, 0) / СоставКомплекта.Количество) >= 1";

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

Пока Выборка.Следующий() Цикл
	КоличествоКомплектов = Цел(Выборка.КоэффициентОбеспеченности);
	Сообщить(Строка(Выборка.Комплект) + ": собрано " + Строка(КоличествоКомплектов) + " шт.");
КонецЦикла;

Разберем работу этого запроса:

  1. Временная таблица ВТ_ТоварыДокумента исключает проблему дублирующихся строк номенклатуры в заказе.
  2. Левое соединение от табличной части справочника рбхКоллекции.Товары гарантирует, что при отсутствии любого из компонентов отношение количества станет равным 0.
  3. Секция ИМЕЮЩИЕ сразу отсекает комплекты, для которых не хватает компонентов даже на один полный набор.
  4. Функция Цел() во встроенном языке корректно отбрасывает остатки без округления вверх.

Вариант 2. Расчет целого числа комплектов целиком внутри запроса

Если требуется получить готовое целое число непосредственно в запросе (например, для использования в динамическом списке или СКД), применим математический сдвиг на 0.4999999999 перед вызовом ВЫРАЗИТЬ. Это эмулирует округление вниз.


ВЫБРАТЬ
	ТЧЗаказа.Номенклатура КАК Номенклатура,
	СУММА(ТЧЗаказа.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_ТоварыЗаказа
ИЗ
	Документ.ЗаказПокупателя.Товары КАК ТЧЗаказа
ГДЕ
	ТЧЗаказа.Ссылка = &Ссылка
СГРУППИРОВАТЬ ПО
	ТЧЗаказа.Номенклатура
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	СоставКомплектов.Ссылка КАК Комплект,
	МИНИМУМ(
		ВЫБОР
			КОГДА ЕСТЬNULL(ВТ_ТоварыЗаказа.Количество, 0) < СоставКомплектов.Количество
				ТОГДА 0
			ИНАЧЕ ВЫРАЗИТЬ((ЕСТЬNULL(ВТ_ТоварыЗаказа.Количество, 0) / СоставКомплектов.Количество) - 0.4999999999 КАК ЧИСЛО(15, 0))
		КОНЕЦ
	) КАК КоличествоКомплектов
ИЗ
	Справочник.рбхКоллекции.Товары КАК СоставКомплектов
	ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТоварыЗаказа КАК ВТ_ТоварыЗаказа
		ПО СоставКомплектов.Номенклатура = ВТ_ТоварыЗаказа.Номенклатура
ГДЕ
	СоставКомплектов.Количество > 0
СГРУППИРОВАТЬ ПО
	СоставКомплектов.Ссылка

Особый случай: один товар входит в несколько комплектов

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

В такой ситуации простой группировкой и взятием минимума задачу не решить. Потребуется программный алгоритм распределения остатков на встроенном языке (жадный алгоритм по приоритету комплектов или рекурсивный перебор вариантов для максимизации итоговой стоимости).

← На главную