В практике автоматизации складского учета часто возникает задача: понять, сколько готовых изделий (в нашем случае — компьютеров) мы можем собрать прямо сейчас, исходя из текущих остатков материалов на складах. Это классическая задача на расчет по спецификации, которая осложняется техническими нюансами языка запросов 1С, такими как обработка отсутствующих остатков и правила округления при делении.
В этой статье мы подробно разберем, как построить надежный запрос для решения этой задачи, проанализируем типичные ошибки новичков и предложим оптимизированный алгоритм работы.
Прежде чем приступать к написанию кода, определим структуру метаданных, с которыми мы работаем. У нас есть:
Компутеры: содержит информацию о моделях компьютеров. У него есть табличная часть Комплектация, где указано, какая Комплектующая (номенклатура) и в каком Количество требуется для сборки одного ПК.ОстаткиМатериалов: хранит данные о фактическом наличии комплектующих на складах (ресурс КоличествоОстаток).Основная проблема заключается в том, что если какой-то детали (например, жесткого диска) нет на складе совсем, то в виртуальной таблице остатков регистра накопления не будет строки с этой деталью. Обычное соединение может просто проигнорировать такой компьютер, в то время как отчет должен показать, что собрать его невозможно (0 штук).
Для того чтобы учесть все необходимые компоненты, мы должны начинать запрос от спецификации (табличной части справочника) и уже к ней присоединять остатки. Используем для этого ЛЕВОЕ СОЕДИНЕНИЕ. Это гарантирует, что даже если остатка по позиции нет, сама потребность в детали попадет в выборку.
Рассмотрим первую часть запроса, где мы подготавливаем данные:
ВЫБРАТЬ
КомпутерыКомплектация.Ссылка КАК Компьютер,
КомпутерыКомплектация.Комплектующая КАК Материал,
КомпутерыКомплектация.Количество КАК Потребность,
ЕСТЬNULL(ОстаткиМатериаловОстатки.КоличествоОстаток, 0) КАК ОстатокНаСкладе,
ОстаткиМатериаловОстатки.Склад КАК Склад
ПОМЕСТИТЬ вт_ДанныеСпецификацииИОстатков
ИЗ
Справочник.Компутеры.Комплектация КАК КомпутерыКомплектация
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиМатериалов.Остатки КАК ОстаткиМатериаловОстатки
ПО КомпутерыКомплектация.Комплектующая = ОстаткиМатериаловОстатки.Материал
;
Обратите внимание на функцию ЕСТЬNULL(..., 0). Это критически важный момент: если запись в регистре отсутствует, мы принудительно заменяем NULL на число 0. Это позволит нам в дальнейшем выполнять арифметические операции без ошибок.
Теперь нам нужно понять, сколько компьютеров можно было бы собрать, если бы нас ограничивала только одна конкретная деталь. Мы делим текущий остаток на количество, необходимое по спецификации.
Проанализируем проблему дробных чисел. Если на складе 5 планок оперативной памяти, а на один компьютер нужно 2, то математически получится 2.5. Однако мы не можем собрать половину компьютера. Нам нужно целое число с округлением вниз.
В языке запросов 1С нет функции Floor (Пол), поэтому мы используем хитрость с функцией ВЫРАЗИТЬ. Вычитая 0.5 из дробного результата и округляя до целого, мы получаем эффект отбрасывания дробной части:
ВЫБРАТЬ
вт_ДанныеСпецификацииИОстатков.Компьютер,
вт_ДанныеСпецификацииИОстатков.Склад,
ВЫБОР
КОГДА вт_ДанныеСпецификацииИОстатков.Потребность > 0
ТОГДА ВЫРАЗИТЬ((вт_ДанныеСпецификацииИОстатков.ОстатокНаСкладе / вт_ДанныеСпецификацииИОстатков.Потребность) - 0.5 КАК ЧИСЛО(15, 0))
ИНАЧЕ 0
КОНЕЦ КАК МожноСобратьДеталей
ПОМЕСТИТЬ вт_РасчетПоКомпонентам
ИЗ
вт_ДанныеСпецификацииИОстатков КАК вт_ДанныеСпецификацииИОстатков
;
Важное замечание: Мы добавили проверку КОГДА Потребность > 0. Это предохраняет запрос от ошибки деления на ноль, если в спецификации по ошибке указано нулевое количество комплектующей.
Общее количество компьютеров, которое можно собрать, определяется тем компонентом, которого меньше всего в пропорции к спецификации. Если у нас процессоров на 100 компьютеров, а корпусов только на 2, то собрать мы сможем всего 2 компьютера. В терминах SQL это означает, что нам нужно найти МИНИМУМ из всех рассчитанных значений в разрезе каждого компьютера.
ВЫБРАТЬ
вт_РасчетПоКомпонентам.Компьютер КАК НаименованиеПК,
вт_РасчетПоКомпонентам.Склад КАК Склад,
МИНИМУМ(вт_РасчетПоКомпонентам.МожноСобратьДеталей) КАК ИтогоМожноСобрать
ИЗ
вт_РасчетПоКомпонентам КАК вт_РасчетПоКомпонентам
ГДЕ
вт_РасчетПоКомпонентам.Склад ЕСТЬ НЕ NULL
СГРУППИРОВАТЬ ПО
вт_РасчетПоКомпонентам.Компьютер,
вт_РасчетПоКомпонентам.Склад
ИМЕЮЩИЕ
МИНИМУМ(вт_РасчетПоКомпонентам.МожноСобратьДеталей) > 0
;
Секция ИМЕЮЩИЕ (HAVING) позволяет нам отсечь те варианты, которые невозможно собрать (где результат равен 0), согласно требованию автора задачи.
Рассмотрим более сложную ситуацию: что делать, если мы хотим видеть расчет по всем складам сразу, даже если на конкретном складе нет ни одной детали? В текущем варианте, если склада нет в регистре остатков, он пропадет из отчета.
Для решения этой проблемы применим Декартово произведение (Cross Join). Сначала создадим временную таблицу всех возможных пар "Компьютер - Склад", а затем будем "навешивать" на нее остатки:
ИЗ или через условие ПО ИСТИНА).Проанализируем также вопрос производительности. Если база данных большая, получение остатков по всему регистру может быть медленным. Рекомендуется передавать в параметры виртуальной таблицы остатков массив только тех материалов, которые реально входят в спецификации компьютеров. Это значительно сократит объем обрабатываемых данных.
Подведем итог. Чтобы ваш отчет работал корректно и отображал реальную картину сборки, придерживайтесь следующих правил:
NULL значения остатков, превращая их в 0.ВЫРАЗИТЬ округляет 0.5 до ближайшего четного). Используйте вычитание 0.5 для корректного получения целой части вниз.МИНИМУМ для поиска компонента, лимитирующего сборку.Применяя данные подходы, вы сможете создать гибкий и точный инструмент для производственного планирования, который не "теряет" данные из-за пустых остатков и корректно обрабатывает количественные показатели.