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