При решении аттестационных задач (например, задачи 1.6 из сборника «1С:Специалист» по платформе) программисты часто сталкиваются с требованием реализовать сложную логику списания партий. Типичное условие звучит так: «Сначала списываем товар с кратчайшим сроком годности, а если сроки совпадают — выбираем наиболее дорогой товар». Несмотря на кажущуюся простоту, реализация этого алгоритма в запросе требует понимания нюансов сортировки, обработки пустых значений и предотвращения исключительных ситуаций.
В этой статье мы подробно разберем, как правильно составить запрос, чтобы механизм УПОРЯДОЧИТЬ ПО работал корректно, и какие подводные камни могут возникнуть в процессе разработки.
Рассмотрим ситуацию: автор темы в своем коде использовал конструкцию УПОРЯДОЧИТЬ ПО СрокГодности, Себестоимость. По умолчанию в языке запросов 1С используется направление ВОЗР (по возрастанию). Для даты (срок годности) это абсолютно верно: мы хотим видеть сначала те товары, срок годности которых истекает раньше (меньшая дата).
Однако для себестоимости условие «списывать более дорогой» означает, что в начале списка должны оказаться позиции с максимальной ценой. Если мы оставим сортировку по умолчанию, программа предложит к списанию сначала самые дешевые партии. Проанализируем, как должен выглядеть правильный блок сортировки:
|УПОРЯДОЧИТЬ ПО
| СрокГодности ВОЗР,
| Себестоимость УБЫВ
Здесь ключевое слово УБЫВ (Descending) заставляет систему выстраивать записи с одинаковым сроком годности от большей цены к меньшей. Это критически важный момент, без которого логика списания будет нарушена.
Выясним причину, по которой расчет себестоимости прямо в запросе может привести к аварийному завершению работы программы. Обычно программисты рассчитывают цену единицы так: СтоимостьОстаток / КоличествоОстаток. Разберем ситуацию: если из-за ошибок в учете или особенностей проведения документов в регистре образовался нулевой остаток по количеству при наличии стоимостного остатка, возникнет ошибка «Деление на ноль».
Чтобы этого избежать, мы всегда должны использовать конструкцию ВЫБОР. Рассмотрим пример правильного вычисления цены в запросе:
|ВЫБОР
| КОГДА ЕСТЬNULL(ОстаткиНоменклатураОстатки.КоличествоОстаток, 0) = 0
| ТОГДА 0
| ИНАЧЕ ОстаткиНоменклатураОстатки.СтоимостьОстаток / ОстаткиНоменклатураОстатки.КоличествоОстаток
|КОНЕЦ КАК ЦенаЕдиницы
Именно поле ЦенаЕдиницы (или рассчитанная на его основе полная себестоимость) должно участвовать в блоке УПОРЯДОЧИТЬ ПО. Обратите внимание, что для целей сортировки не обязательно умножать цену на количество в документе — относительный порядок партий от этого не изменится, но запрос станет чище.
Поскольку мы получаем остатки с помощью ЛЕВОЕ СОЕДИНЕНИЕ, для товаров, которых нет на складе, виртуальная таблица вернет NULL во всех полях. Проанализируем, как это влияет на сортировку. В 1С значение NULL считается «минимальным». Это означает, что при сортировке ВОЗР записи с NULL в сроке годности могут оказаться в самом начале, что нелогично для списания.
Рекомендуется использовать функцию ЕСТЬNULL(), чтобы подставить «безопасные» значения для сортировки. Для срока годности это может быть пустая дата (если мы хотим видеть их первыми) или очень далекая дата в будущем.
|ВЫБОР
| КОГДА ОстаткиНоменклатураОстатки.СрокГодности ЕСТЬ NULL
| ТОГДА ДАТАВРЕМЯ(3999, 12, 31)
| ИНАЧЕ ОстаткиНоменклатураОстатки.СрокГодности
|КОНЕЦ КАК СрокГодностиДляСортировки
В задачах на списание партий практически всегда используется предложение ИТОГИ ПО. Посмотрим на пример структуры запроса:
|ИЗ
| ВТ_ТоварыДокумента КАК Товары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Остатки.Остатки(&Период, ) КАК Остатки
| ПО Товары.Номенклатура = Остатки.Номенклатура
|
|УПОРЯДОЧИТЬ ПО
| Товары.Номенклатура,
| СрокГодности ВОЗР,
| Цена УБЫВ
|ИТОГИ
| МАКСИМУМ(КоличествоВДокументе),
| СУММА(КоличествоОстаток)
|ПО
| Номенклатура
Важно помнить: когда мы используем ИТОГИ, блок УПОРЯДОЧИТЬ ПО управляет порядком внутри группировок. При обходе результата запроса в коде 1С (через Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам)) мы сначала получим строку номенклатуры, а затем, вызвав Выбрать() для этой строки, получим детальные записи (партии) именно в том порядке, который задали: сначала по сроку, потом по убыванию цены.
Подведем итог. Для корректной реализации алгоритма FEFO с приоритетом дорогого товара нам необходимо:
УБЫВ для поля стоимости в секции УПОРЯДОЧИТЬ ПО.ВЫБОР.ЛЕВОЕ СОЕДИНЕНИЕ требует обработки NULL, чтобы пустые остатки не искажали логику сортировки.ИТОГИ ПО Номенклатура для удобного сопоставления потребности из документа и фактического наличия партий на складе.Такой подход не только гарантирует правильное решение задачи, но и делает код устойчивым к аномалиям в данных (отсутствие остатков, нулевые значения), что крайне важно для реальных учетных систем.